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 /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
533 { FormulaMapGroupSpecialOffset::MACRO
, ocMacro
} ,
534 { FormulaMapGroupSpecialOffset::COL_ROW_NAME
, ocColRowName
} ,
535 { FormulaMapGroupSpecialOffset::WHITESPACE
, ocWhitespace
}
537 const size_t nCount
= SAL_N_ELEMENTS(aMap
);
538 // Preallocate vector elements.
539 FormulaOpCodeMapEntry aEntry
;
540 aEntry
.Token
.OpCode
= getOpCodeUnknown();
541 aVec
.resize(nCount
, aEntry
);
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
[] = {
632 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
633 // functions with 2 or more parameters.
634 for (sal_uInt16 nOp
= SC_OPCODE_START_2_PAR
; nOp
< SC_OPCODE_STOP_2_PAR
&& nOp
< mnSymbols
; ++nOp
)
638 // NO_NAME is in SPECIAL.
639 case SC_OPCODE_NO_NAME
:
642 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
645 // If AddIn functions are present in this mapping, use them, and only those.
648 for (auto const& elem
: maExternalHashMap
)
650 FormulaOpCodeMapEntry aEntry
;
651 aEntry
.Name
= elem
.first
;
652 aEntry
.Token
.Data
<<= elem
.second
;
653 aEntry
.Token
.OpCode
= ocExternal
;
654 aVec
.push_back( aEntry
);
659 rCompiler
.fillAddInToken( aVec
, isEnglish());
663 return uno::Sequence
< FormulaOpCodeMapEntry
>(aVec
.data(), aVec
.size());
667 void FormulaCompiler::OpCodeMap::putOpCode( const OUString
& rStr
, const OpCode eOp
, const CharClass
* pCharClass
)
669 if (0 < eOp
&& sal_uInt16(eOp
) < mnSymbols
)
671 bool bPutOp
= mpTable
[eOp
].isEmpty();
672 bool bRemoveFromMap
= false;
677 // These OpCodes are meant to overwrite and also remove an
681 bRemoveFromMap
= true;
683 // These separator OpCodes are meant to overwrite and also
684 // remove an existing mapping if it is not used for one of the
688 bRemoveFromMap
= (mpTable
[ocArrayRowSep
] != mpTable
[eOp
] && mpTable
[ocSep
] != mpTable
[eOp
]);
692 bRemoveFromMap
= (mpTable
[ocArrayColSep
] != mpTable
[eOp
] && mpTable
[ocSep
] != mpTable
[eOp
]);
694 // For ocSep keep the ";" in map but remove any other if it is
695 // not used for ocArrayColSep or ocArrayRowSep.
698 bRemoveFromMap
= (mpTable
[eOp
] != ";" &&
699 mpTable
[ocArrayColSep
] != mpTable
[eOp
] &&
700 mpTable
[ocArrayRowSep
] != mpTable
[eOp
]);
702 // These OpCodes are known to be duplicates in the Excel
703 // external API mapping because of different parameter counts
704 // in different BIFF versions. Names are identical and entries
715 case ocGetDiffDate360
:
716 if (rStr
== mpTable
[eOp
])
719 // These OpCodes are known to be added to an existing mapping,
720 // but only for the OOXML external API mapping. This is *not*
721 // FormulaLanguage::OOXML. Keep the first
722 // (correct) definition for the OpCode, all following are
723 // additional alias entries in the map.
731 FormulaGrammar::extractFormulaLanguage( meGrammar
) == FormulaGrammar::GRAM_EXTERNAL
)
733 // Both bPutOp and bRemoveFromMap stay false.
738 SAL_WARN("formula.core",
739 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16
>(eOp
)
740 << ", replacing '" << mpTable
[eOp
] << "' with '" << rStr
<< "' in "
741 << (mbEnglish
? "" : "non-") << "English map 0x" << ::std::hex
<< meGrammar
);
745 // Case preserving opcode -> string, upper string -> opcode
748 OUString
aUpper( pCharClass
? pCharClass
->uppercase( mpTable
[eOp
]) : rStr
.toAsciiUpperCase());
749 // Ensure we remove a mapping only for the requested OpCode.
750 OpCodeHashMap::const_iterator
it( maHashMap
.find( aUpper
));
751 if (it
!= maHashMap
.end() && (*it
).second
== eOp
)
752 maHashMap
.erase( it
);
756 OUString
aUpper( pCharClass
? pCharClass
->uppercase( rStr
) : rStr
.toAsciiUpperCase());
757 maHashMap
.emplace(aUpper
, eOp
);
761 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
766 FormulaCompiler::FormulaCompiler( FormulaTokenArray
& rArr
, bool bComputeII
, bool bMatrixFlag
)
768 nCurrentFactorParam(0),
770 maArrIterator( rArr
),
775 nNumFmt( SvNumFormatType::UNDEFINED
),
777 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
778 bAutoCorrect( false ),
781 needsRPNTokenCheck( false ),
782 mbJumpCommandReorder(true),
784 mbComputeII(bComputeII
),
785 mbMatrixFlag(bMatrixFlag
)
789 FormulaTokenArray
FormulaCompiler::smDummyTokenArray
;
791 FormulaCompiler::FormulaCompiler(bool bComputeII
, bool bMatrixFlag
)
793 nCurrentFactorParam(0),
795 maArrIterator( smDummyTokenArray
),
800 nNumFmt( SvNumFormatType::UNDEFINED
),
802 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
803 bAutoCorrect( false ),
806 needsRPNTokenCheck( false ),
807 mbJumpCommandReorder(true),
809 mbComputeII(bComputeII
),
810 mbMatrixFlag(bMatrixFlag
)
814 FormulaCompiler::~FormulaCompiler()
818 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage
) const
820 const bool bTemporary
= !HasOpCodeMap(nLanguage
);
821 OpCodeMapPtr xMap
= GetFinalOpCodeMap(nLanguage
);
823 const_cast<FormulaCompiler
*>(this)->DestroyOpCodeMap(nLanguage
);
827 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::GetFinalOpCodeMap( const sal_Int32 nLanguage
) const
829 FormulaCompiler::OpCodeMapPtr xMap
;
830 using namespace sheet
;
833 case FormulaLanguage::ODFF
:
835 InitSymbolsODFF( InitSymbols::INIT
);
836 xMap
= mxSymbolsODFF
;
838 case FormulaLanguage::ODF_11
:
840 InitSymbolsPODF( InitSymbols::INIT
);
841 xMap
= mxSymbolsPODF
;
843 case FormulaLanguage::ENGLISH
:
844 if (!mxSymbolsEnglish
)
845 InitSymbolsEnglish( InitSymbols::INIT
);
846 xMap
= mxSymbolsEnglish
;
848 case FormulaLanguage::NATIVE
:
849 if (!mxSymbolsNative
)
850 InitSymbolsNative( InitSymbols::INIT
);
851 xMap
= mxSymbolsNative
;
853 case FormulaLanguage::XL_ENGLISH
:
854 if (!mxSymbolsEnglishXL
)
855 InitSymbolsEnglishXL( InitSymbols::INIT
);
856 xMap
= mxSymbolsEnglishXL
;
858 case FormulaLanguage::OOXML
:
860 InitSymbolsOOXML( InitSymbols::INIT
);
861 xMap
= mxSymbolsOOXML
;
863 case FormulaLanguage::API
:
865 InitSymbolsAPI( InitSymbols::INIT
);
869 ; // nothing, NULL map returned
874 void FormulaCompiler::DestroyOpCodeMap( const sal_Int32 nLanguage
)
876 using namespace sheet
;
879 case FormulaLanguage::ODFF
:
880 InitSymbolsODFF( InitSymbols::DESTROY
);
882 case FormulaLanguage::ODF_11
:
883 InitSymbolsPODF( InitSymbols::DESTROY
);
885 case FormulaLanguage::ENGLISH
:
886 InitSymbolsEnglish( InitSymbols::DESTROY
);
888 case FormulaLanguage::NATIVE
:
889 InitSymbolsNative( InitSymbols::DESTROY
);
891 case FormulaLanguage::XL_ENGLISH
:
892 InitSymbolsEnglishXL( InitSymbols::DESTROY
);
894 case FormulaLanguage::OOXML
:
895 InitSymbolsOOXML( InitSymbols::DESTROY
);
897 case FormulaLanguage::API
:
898 InitSymbolsAPI( InitSymbols::DESTROY
);
905 bool FormulaCompiler::HasOpCodeMap( const sal_Int32 nLanguage
) const
907 using namespace sheet
;
910 case FormulaLanguage::ODFF
:
911 return InitSymbolsODFF( InitSymbols::ASK
);
912 case FormulaLanguage::ODF_11
:
913 return InitSymbolsPODF( InitSymbols::ASK
);
914 case FormulaLanguage::ENGLISH
:
915 return InitSymbolsEnglish( InitSymbols::ASK
);
916 case FormulaLanguage::NATIVE
:
917 return InitSymbolsNative( InitSymbols::ASK
);
918 case FormulaLanguage::XL_ENGLISH
:
919 return InitSymbolsEnglishXL( InitSymbols::ASK
);
920 case FormulaLanguage::OOXML
:
921 return InitSymbolsOOXML( InitSymbols::ASK
);
922 case FormulaLanguage::API
:
923 return InitSymbolsAPI( InitSymbols::ASK
);
930 OUString
FormulaCompiler::FindAddInFunction( const OUString
& /*rUpperName*/, bool /*bLocalFirst*/ ) const
935 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::CreateOpCodeMap(
937 const sheet::FormulaOpCodeMapEntry
> & rMapping
,
940 using sheet::FormulaOpCodeMapEntry
;
941 // Filter / API maps are never Core
942 NonConstOpCodeMapPtr xMap
= std::make_shared
<OpCodeMap
>( SC_OPCODE_LAST_OPCODE_ID
+ 1, false,
943 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
944 FormulaGrammar::GRAM_EXTERNAL
, bEnglish
), FormulaGrammar::CONV_UNSPECIFIED
));
945 std::unique_ptr
<CharClass
> xCharClass( xMap
->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
946 const CharClass
* pCharClass
= xCharClass
.get();
947 for (auto const& rMapEntry
: rMapping
)
949 OpCode eOp
= OpCode(rMapEntry
.Token
.OpCode
);
950 if (eOp
!= ocExternal
)
951 xMap
->putOpCode( rMapEntry
.Name
, eOp
, pCharClass
);
954 OUString aExternalName
;
955 if (rMapEntry
.Token
.Data
>>= aExternalName
)
956 xMap
->putExternal( rMapEntry
.Name
, aExternalName
);
959 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
966 static bool lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, FormulaCompiler::InitSymbols eWhat
= FormulaCompiler::InitSymbols::INIT
)
968 static OpCodeMapData aSymbolMap
;
969 static std::map
<OUString
, OpCodeMapData
> aLocaleSymbolMap
;
970 std::unique_lock
aGuard(aSymbolMap
.maMtx
);
972 if (comphelper::LibreOfficeKit::isActive())
974 OUString langauge
= comphelper::LibreOfficeKit::getLanguageTag().getLanguage();
975 if (eWhat
== FormulaCompiler::InitSymbols::ASK
)
977 return aLocaleSymbolMap
.contains(langauge
)
978 && bool(aLocaleSymbolMap
[langauge
].mxSymbolMap
);
980 else if (eWhat
== FormulaCompiler::InitSymbols::DESTROY
)
982 aLocaleSymbolMap
[langauge
].mxSymbolMap
.reset();
984 else if (!aLocaleSymbolMap
[langauge
].mxSymbolMap
)
987 aLocaleSymbolMap
[langauge
].mxSymbolMap
= std::make_shared
<FormulaCompiler::OpCodeMap
>(
988 SC_OPCODE_LAST_OPCODE_ID
+ 1, true, FormulaGrammar::GRAM_NATIVE_UI
);
989 OpCodeList
aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS
,
990 aLocaleSymbolMap
[langauge
].mxSymbolMap
);
991 OpCodeList
aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES
,
992 aLocaleSymbolMap
[langauge
].mxSymbolMap
);
993 // No AddInMap for native core mapping.
996 xMap
= aLocaleSymbolMap
[langauge
].mxSymbolMap
;
1000 if (eWhat
== FormulaCompiler::InitSymbols::ASK
)
1002 return bool(aSymbolMap
.mxSymbolMap
);
1004 else if (eWhat
== FormulaCompiler::InitSymbols::DESTROY
)
1006 aSymbolMap
.mxSymbolMap
.reset();
1008 else if (!aSymbolMap
.mxSymbolMap
)
1011 aSymbolMap
.mxSymbolMap
= std::make_shared
<FormulaCompiler::OpCodeMap
>(
1012 SC_OPCODE_LAST_OPCODE_ID
+ 1, true, FormulaGrammar::GRAM_NATIVE_UI
);
1013 OpCodeList
aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS
,
1014 aSymbolMap
.mxSymbolMap
);
1015 OpCodeList
aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES
, aSymbolMap
.mxSymbolMap
);
1016 // No AddInMap for native core mapping.
1019 xMap
= aSymbolMap
.mxSymbolMap
;
1025 const OUString
& FormulaCompiler::GetNativeSymbol( OpCode eOp
)
1027 NonConstOpCodeMapPtr xSymbolsNative
;
1028 lcl_fillNativeSymbols( xSymbolsNative
);
1029 return xSymbolsNative
->getSymbol( eOp
);
1032 sal_Unicode
FormulaCompiler::GetNativeSymbolChar( OpCode eOp
)
1034 return GetNativeSymbol(eOp
)[0];
1037 bool FormulaCompiler::InitSymbolsNative( FormulaCompiler::InitSymbols eWhat
) const
1039 return lcl_fillNativeSymbols( mxSymbolsNative
, eWhat
);
1042 bool FormulaCompiler::InitSymbolsEnglish( FormulaCompiler::InitSymbols eWhat
) const
1044 static OpCodeMapData aMap
;
1045 std::unique_lock
aGuard(aMap
.maMtx
);
1046 if (eWhat
== InitSymbols::ASK
)
1047 return bool(aMap
.mxSymbolMap
);
1048 else if (eWhat
== InitSymbols::DESTROY
)
1049 aMap
.mxSymbolMap
.reset();
1050 else if (!aMap
.mxSymbolMap
)
1051 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
1052 mxSymbolsEnglish
= aMap
.mxSymbolMap
;
1056 bool FormulaCompiler::InitSymbolsPODF( FormulaCompiler::InitSymbols eWhat
) const
1058 static OpCodeMapData aMap
;
1059 std::unique_lock
aGuard(aMap
.maMtx
);
1060 if (eWhat
== InitSymbols::ASK
)
1061 return bool(aMap
.mxSymbolMap
);
1062 else if (eWhat
== InitSymbols::DESTROY
)
1063 aMap
.mxSymbolMap
.reset();
1064 else if (!aMap
.mxSymbolMap
)
1065 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF
, FormulaGrammar::GRAM_PODF
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1066 mxSymbolsPODF
= aMap
.mxSymbolMap
;
1070 bool FormulaCompiler::InitSymbolsAPI( FormulaCompiler::InitSymbols eWhat
) const
1072 static OpCodeMapData aMap
;
1073 std::unique_lock
aGuard(aMap
.maMtx
);
1074 if (eWhat
== InitSymbols::ASK
)
1075 return bool(aMap
.mxSymbolMap
);
1076 else if (eWhat
== InitSymbols::DESTROY
)
1077 aMap
.mxSymbolMap
.reset();
1078 else if (!aMap
.mxSymbolMap
)
1079 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API
, FormulaGrammar::GRAM_API
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1080 mxSymbolsAPI
= aMap
.mxSymbolMap
;
1084 bool FormulaCompiler::InitSymbolsODFF( FormulaCompiler::InitSymbols eWhat
) const
1086 static OpCodeMapData aMap
;
1087 std::unique_lock
aGuard(aMap
.maMtx
);
1088 if (eWhat
== InitSymbols::ASK
)
1089 return bool(aMap
.mxSymbolMap
);
1090 else if (eWhat
== InitSymbols::DESTROY
)
1091 aMap
.mxSymbolMap
.reset();
1092 else if (!aMap
.mxSymbolMap
)
1093 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF
, FormulaGrammar::GRAM_ODFF
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1094 mxSymbolsODFF
= aMap
.mxSymbolMap
;
1098 bool FormulaCompiler::InitSymbolsEnglishXL( FormulaCompiler::InitSymbols eWhat
) const
1100 static OpCodeMapData aMap
;
1101 std::unique_lock
aGuard(aMap
.maMtx
);
1102 if (eWhat
== InitSymbols::ASK
)
1103 return bool(aMap
.mxSymbolMap
);
1104 else if (eWhat
== InitSymbols::DESTROY
)
1105 aMap
.mxSymbolMap
.reset();
1106 else if (!aMap
.mxSymbolMap
)
1107 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
1108 mxSymbolsEnglishXL
= aMap
.mxSymbolMap
;
1109 if (eWhat
!= InitSymbols::INIT
)
1112 // TODO: For now, just replace the separators to the Excel English
1113 // variants. Later, if we want to properly map Excel functions with Calc
1114 // functions, we'll need to do a little more work here.
1115 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocSep
, nullptr);
1116 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocArrayColSep
, nullptr);
1117 mxSymbolsEnglishXL
->putOpCode( OUString(';'), ocArrayRowSep
, nullptr);
1122 bool FormulaCompiler::InitSymbolsOOXML( FormulaCompiler::InitSymbols eWhat
) const
1124 static OpCodeMapData aMap
;
1125 std::unique_lock
aGuard(aMap
.maMtx
);
1126 if (eWhat
== InitSymbols::ASK
)
1127 return bool(aMap
.mxSymbolMap
);
1128 else if (eWhat
== InitSymbols::DESTROY
)
1129 aMap
.mxSymbolMap
.reset();
1130 else if (!aMap
.mxSymbolMap
)
1131 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML
, FormulaGrammar::GRAM_OOXML
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1132 mxSymbolsOOXML
= aMap
.mxSymbolMap
;
1137 void FormulaCompiler::loadSymbols(const std::pair
<const char*, int>* pSymbols
, FormulaGrammar::Grammar eGrammar
,
1138 NonConstOpCodeMapPtr
& rxMap
, SeparatorType eSepType
) const
1144 rxMap
= std::make_shared
<OpCodeMap
>( SC_OPCODE_LAST_OPCODE_ID
+ 1, eGrammar
!= FormulaGrammar::GRAM_ODFF
, eGrammar
);
1145 OpCodeList
aOpCodeList(pSymbols
, rxMap
, eSepType
);
1147 fillFromAddInMap( rxMap
, eGrammar
);
1148 // Fill from collection for AddIns not already present.
1149 if (FormulaGrammar::GRAM_ENGLISH
== eGrammar
)
1150 fillFromAddInCollectionEnglishName( rxMap
);
1153 fillFromAddInCollectionUpperName( rxMap
);
1154 if (FormulaGrammar::GRAM_API
== eGrammar
)
1156 // Add known but not in AddInMap English names, e.g. from the
1157 // PricingFunctions AddIn or any user supplied AddIn.
1158 fillFromAddInCollectionEnglishName( rxMap
);
1163 void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr
& /*xMap */) const
1167 void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr
& /*xMap */) const
1171 void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr
& /*xMap*/, FormulaGrammar::Grammar
/*_eGrammar */) const
1175 OpCode
FormulaCompiler::GetEnglishOpCode( const OUString
& rName
) const
1177 FormulaCompiler::OpCodeMapPtr xMap
= GetOpCodeMap( sheet::FormulaLanguage::ENGLISH
);
1179 formula::OpCodeHashMap::const_iterator
iLook( xMap
->getHashMap().find( rName
) );
1180 bool bFound
= (iLook
!= xMap
->getHashMap().end());
1181 return bFound
? (*iLook
).second
: ocNone
;
1184 bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp
)
1196 // more than one parameters:
1197 // ocIndirect otherwise would have to do
1198 // StopListening and StartListening on a reference for every
1199 // interpreted value.
1201 // ocOffset results in indirect references.
1203 // ocDebugVar shows internal value that may change as the internal state changes.
1214 bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp
)
1229 // Remove quotes, escaped quotes are unescaped.
1230 bool FormulaCompiler::DeQuote( OUString
& rStr
)
1232 sal_Int32 nLen
= rStr
.getLength();
1233 if ( nLen
> 1 && rStr
[0] == '\'' && rStr
[ nLen
-1 ] == '\'' )
1235 rStr
= rStr
.copy( 1, nLen
-2 );
1236 rStr
= rStr
.replaceAll( "''", "'" );
1242 void FormulaCompiler::fillAddInToken(
1243 ::std::vector
< sheet::FormulaOpCodeMapEntry
>& /*_rVec*/,
1244 bool /*_bIsEnglish*/) const
1248 bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode
)
1262 case ocModalValue_Multi
:
1270 // added to avoid warnings
1277 void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString
& rSymbol
, OpCode eOp
)
1279 SAL_WARN_IF( !mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty(), "formula.core",
1280 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16
>(eOp
)
1281 << " '" << mpTable
[eOp
] << "' with empty name!");
1282 if (!mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty())
1283 maHashMap
.emplace(mpTable
[eOp
], eOp
);
1286 mpTable
[eOp
] = rSymbol
;
1287 maHashMap
.emplace(rSymbol
, eOp
);
1291 void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap
& r
)
1293 maHashMap
= OpCodeHashMap( mnSymbols
);
1295 sal_uInt16 n
= r
.getSymbolCount();
1296 SAL_WARN_IF( n
!= mnSymbols
, "formula.core",
1297 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols
<< " that: " << n
);
1301 // OpCode 0 (ocPush) should never be in a map.
1302 SAL_WARN_IF( !mpTable
[0].isEmpty() || !r
.mpTable
[0].isEmpty(), "formula.core",
1303 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
1304 << mpTable
[0] << "' that: '" << r
.mpTable
[0] << "'");
1306 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
1307 // and API) to the native map (UI "use English function names") replace the
1308 // known bad legacy function names with correct ones.
1310 FormulaGrammar::extractFormulaLanguage( meGrammar
) == sheet::FormulaLanguage::NATIVE
&&
1311 FormulaGrammar::extractFormulaLanguage( r
.meGrammar
) == sheet::FormulaLanguage::ENGLISH
)
1313 for (sal_uInt16 i
= 1; i
< n
; ++i
)
1316 OpCode eOp
= OpCode(i
);
1323 aSymbol
= "MULTIPLE.OPERATIONS";
1326 aSymbol
= r
.mpTable
[i
];
1328 putCopyOpCode( aSymbol
, eOp
);
1333 for (sal_uInt16 i
= 1; i
< n
; ++i
)
1335 OpCode eOp
= OpCode(i
);
1336 const OUString
& rSymbol
= r
.mpTable
[i
];
1337 putCopyOpCode( rSymbol
, eOp
);
1341 // This was meant to copy to native map that does not have AddIn symbols
1342 // but needs them from the source map. It is unclear what should happen if
1343 // the destination already had externals, so do it only if it doesn't.
1344 if (!hasExternals())
1346 maExternalHashMap
= r
.maExternalHashMap
;
1347 maReverseExternalHashMap
= r
.maReverseExternalHashMap
;
1349 if (mbEnglish
!= r
.mbEnglish
)
1351 // For now keep mbEnglishLocale setting, which is false for a
1352 // non-English native map we're copying to.
1354 if (!mbEnglish && r.mbEnglish)
1355 mbEnglishLocale = "getUseEnglishLocaleFromConfiguration()";
1356 or set from outside i.e. via ScCompiler.
1358 mbEnglish
= r
.mbEnglish
;
1364 FormulaError
FormulaCompiler::GetErrorConstant( const OUString
& rName
) const
1366 FormulaError nError
= FormulaError::NONE
;
1367 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
));
1368 if (iLook
!= mxSymbols
->getHashMap().end())
1370 switch ((*iLook
).second
)
1372 // Not all may make sense in a formula, but these we know as
1375 nError
= FormulaError::NoCode
;
1378 nError
= FormulaError::DivisionByZero
;
1381 nError
= FormulaError::NoValue
;
1384 nError
= FormulaError::NoRef
;
1387 nError
= FormulaError::NoName
;
1390 nError
= FormulaError::IllegalFPOperation
;
1393 nError
= FormulaError::NotAvailable
;
1401 // Per convention recognize detailed "#ERRxxx!" constants, always
1402 // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
1404 if (rName
.startsWithIgnoreAsciiCase("#ERR") && rName
.getLength() <= 10 && rName
[rName
.getLength()-1] == '!')
1406 sal_uInt32 nErr
= o3tl::toUInt32(rName
.subView( 4, rName
.getLength() - 5));
1407 if (0 < nErr
&& nErr
<= SAL_MAX_UINT16
&& isPublishedFormulaError(static_cast<FormulaError
>(nErr
)))
1408 nError
= static_cast<FormulaError
>(nErr
);
1414 void FormulaCompiler::EnableJumpCommandReorder( bool bEnable
)
1416 mbJumpCommandReorder
= bEnable
;
1419 void FormulaCompiler::EnableStopOnError( bool bEnable
)
1421 mbStopOnError
= bEnable
;
1424 void FormulaCompiler::AppendErrorConstant( OUStringBuffer
& rBuffer
, FormulaError nError
) const
1429 case FormulaError::NoCode
:
1432 case FormulaError::DivisionByZero
:
1435 case FormulaError::NoValue
:
1438 case FormulaError::NoRef
:
1441 case FormulaError::NoName
:
1444 case FormulaError::IllegalFPOperation
:
1447 case FormulaError::NotAvailable
:
1452 // Per convention create detailed "#ERRxxx!" constants, always
1454 rBuffer
.append("#ERR");
1455 rBuffer
.append(static_cast<sal_Int32
>(nError
));
1456 rBuffer
.append('!');
1460 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
1463 constexpr short nRecursionMax
= 100;
1465 bool FormulaCompiler::GetToken()
1467 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
1468 if ( nRecursion
> nRecursionMax
)
1470 SetError( FormulaError::StackOverflow
);
1471 mpLastToken
= mpToken
= new FormulaByteToken( ocStop
);
1474 if ( bAutoCorrect
&& !pStack
)
1475 { // don't merge stacked subroutine code into entered formula
1476 aCorrectedFormula
+= aCorrectedSymbol
;
1477 aCorrectedSymbol
.clear();
1480 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
1484 FormulaTokenRef pSpacesToken
;
1485 short nWasColRowName
;
1486 if ( pArr
->OpCodeBefore( maArrIterator
.GetIndex() ) == ocColRowName
)
1491 mpToken
= maArrIterator
.Next();
1492 while (mpToken
&& ((eTmpOp
= mpToken
->GetOpCode()) == ocSpaces
|| eTmpOp
== ocWhitespace
))
1494 if (eTmpOp
== ocSpaces
)
1496 // For significant whitespace remember last ocSpaces token.
1497 // Usually there's only one even for multiple spaces.
1498 pSpacesToken
= mpToken
;
1499 if ( nWasColRowName
)
1502 if ( bAutoCorrect
&& !pStack
)
1503 CreateStringFromToken( aCorrectedFormula
, mpToken
.get() );
1504 mpToken
= maArrIterator
.Next();
1506 if ( bAutoCorrect
&& !pStack
&& mpToken
)
1507 CreateStringFromToken( aCorrectedSymbol
, mpToken
.get() );
1513 // mpLastToken was popped as well and corresponds to the
1514 // then current last token during PushTokenArray(), e.g. for
1523 if ( nWasColRowName
>= 2 && mpToken
->GetOpCode() == ocColRowName
)
1524 { // convert an ocSpaces to ocIntersect in RPN
1525 mpLastToken
= mpToken
= new FormulaByteToken( ocIntersect
);
1526 maArrIterator
.StepBack(); // we advanced to the second ocColRowName, step back
1528 else if (pSpacesToken
&& FormulaGrammar::isExcelSyntax( meGrammar
) &&
1529 mpLastToken
&& mpToken
&&
1530 isPotentialRangeType( mpLastToken
.get(), false, false) &&
1531 isPotentialRangeType( mpToken
.get(), false, true))
1533 // Let IntersectionLine() <- Factor() decide how to treat this,
1534 // once the actual arguments are determined in RPN.
1535 mpLastToken
= mpToken
= pSpacesToken
;
1536 maArrIterator
.StepBack(); // step back from next non-spaces token
1543 mpLastToken
= mpToken
= new FormulaByteToken( ocStop
);
1547 // Remember token for next round and any PushTokenArray() calls that may
1548 // occur in handlers.
1549 mpLastToken
= mpToken
;
1551 if ( mpToken
->IsExternalRef() )
1553 return HandleExternalReference(*mpToken
);
1557 switch (mpToken
->GetOpCode())
1566 // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
1567 // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
1568 needsRPNTokenCheck
= true;
1573 return HandleColRowName();
1575 return HandleDbData();
1577 return HandleTableRef();
1580 HandleIIOpCode(mpToken
.get(), nullptr, 0);
1590 // RPN creation by recursion
1591 void FormulaCompiler::Factor()
1593 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
1596 CurrentFactor
pFacToken( this );
1598 OpCode eOp
= mpToken
->GetOpCode();
1599 if (eOp
== ocPush
|| eOp
== ocColRowNameAuto
|| eOp
== ocMatRef
|| eOp
== ocDBArea
1600 || eOp
== ocTableRef
1601 || (!mbJumpCommandReorder
&& ((eOp
== ocName
) || (eOp
== ocColRowName
) || (eOp
== ocBad
)))
1608 // PUSH( is an error that may be caused by an unknown function.
1610 ( mpToken
->GetType() == svString
1611 || mpToken
->GetType() == svSingleRef
)
1612 ? FormulaError::NoName
: FormulaError::OperatorExpected
);
1613 if ( bAutoCorrect
&& !pStack
)
1614 { // assume multiplication
1615 aCorrectedFormula
+= mxSymbols
->getSymbol( ocMul
);
1619 if( eOp
!= ocClose
)
1620 SetError( FormulaError::PairExpected
);
1626 else if( eOp
== ocOpen
)
1630 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1631 { // range list (A1;A2) converted to (A1~A2)
1632 pFacToken
= mpToken
;
1634 CheckSetForceArrayParameter( mpToken
, 0);
1636 // Do not ignore error here, regardless of mbStopOnError, to not
1637 // change the formula expression in case of an unexpected state.
1638 if (pArr
->GetCodeError() == FormulaError::NONE
&& pc
>= 2)
1640 // Left and right operands must be reference or function
1641 // returning reference to form a range list.
1642 const FormulaToken
* p
= pCode
[-2];
1643 if (p
&& isPotentialRangeType( p
, true, false))
1646 if (p
&& isPotentialRangeType( p
, true, true))
1648 pFacToken
->NewOpCode( ocUnion
, FormulaToken::PrivateAccess());
1649 // XXX NOTE: the token's eType is still svSep here!
1650 PutCode( pFacToken
);
1656 SetError( FormulaError::PairExpected
);
1660 /* TODO: if no conversion to ocUnion is involved this could collect
1661 * such expression as a list or (matrix) vector to be passed as
1662 * argument for one parameter (which in fact the ocUnion svRefList is a
1663 * special case of), which would require a new StackVar type and needed
1664 * to be handled by the interpreter for functions that could support it
1665 * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
1666 * not defined by ODF.
1667 * Does Excel handle =SUM((1;2))?
1668 * As is, the interpreter catches extraneous uncalculated
1669 * subexpressions like 1 of (1;2) as error. */
1673 if( nNumFmt
== SvNumFormatType::UNDEFINED
)
1674 nNumFmt
= lcl_GetRetFormat( eOp
);
1676 if ( IsOpCodeVolatile( eOp
) )
1677 pArr
->SetExclusiveRecalcModeAlways();
1682 // Functions recalculated on every document load.
1683 // ONLOAD_LENIENT here to be able to distinguish and not
1684 // force a recalc (if not in an ALWAYS or ONLOAD_MUST
1685 // context) but keep an imported result from for example
1686 // OOXML a DDE call. Will be recalculated for ODFF.
1691 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT
);
1693 // RANDBETWEEN() is volatile like RAND(). Other Add-In
1694 // functions may have to be recalculated or not, we don't
1695 // know, classify as ONLOAD_LENIENT.
1697 if (mpToken
->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
1698 pArr
->SetExclusiveRecalcModeAlways();
1700 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT
);
1702 // If the referred cell is moved the value changes.
1705 pArr
->SetRecalcModeOnRefMove();
1707 // ocCell needs recalc on move for some possible type values.
1708 // And recalc mode on load, tdf#60645
1710 pArr
->SetRecalcModeOnRefMove();
1711 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_MUST
);
1714 // Cell with hyperlink needs to be calculated on load to
1715 // get its matrix result generated.
1716 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_MUST
);
1717 pArr
->SetHyperLink( true);
1723 if (SC_OPCODE_START_NO_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_NO_PAR
)
1725 pFacToken
= mpToken
;
1729 SetError( FormulaError::PairExpected
);
1730 PutCode( pFacToken
);
1736 SetError( FormulaError::PairExpected
);
1737 PutCode( pFacToken
);
1741 else if (SC_OPCODE_START_1_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_1_PAR
)
1743 if (eOp
== ocIsoWeeknum
&& FormulaGrammar::isODFF( meGrammar
))
1745 // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
1746 // the opcode then has to be changed to ocWeek for backward compatibility
1747 pFacToken
= mpToken
;
1749 bool bNoParam
= false;
1757 CheckSetForceArrayParameter( mpToken
, 0);
1762 SetError( FormulaError::PairExpected
);
1763 sal_uInt32 nSepCount
= 0;
1764 const sal_uInt16 nSepPos
= maArrIterator
.GetIndex() - 1; // separator position, if any
1768 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1771 CheckSetForceArrayParameter( mpToken
, nSepCount
);
1773 if (nSepCount
> FORMULA_MAXPARAMS
)
1774 SetError( FormulaError::CodeOverflow
);
1779 SetError( FormulaError::PairExpected
);
1782 pFacToken
->SetByte( nSepCount
);
1785 // An old mode!=1 indicates ISO week, remove argument if
1786 // literal double value and keep function. Anything else
1787 // can not be resolved, there exists no "like ISO but week
1788 // starts on Sunday" mode in WEEKNUM and for an expression
1789 // we can't determine.
1790 // Current index is nSepPos+3 if expression stops, or
1791 // nSepPos+4 if expression continues after the call because
1792 // we just called NextToken() to move away from it.
1793 if (pc
>= 2 && (maArrIterator
.GetIndex() == nSepPos
+ 3 || maArrIterator
.GetIndex() == nSepPos
+ 4) &&
1794 pArr
->TokenAt(nSepPos
+1)->GetType() == svDouble
&&
1795 pArr
->TokenAt(nSepPos
+1)->GetDouble() != 1.0 &&
1796 pArr
->TokenAt(nSepPos
+2)->GetOpCode() == ocClose
&&
1797 pArr
->RemoveToken( nSepPos
, 2) == 2)
1799 maArrIterator
.AfterRemoveToken( nSepPos
, 2);
1800 // Remove the ocPush/svDouble just removed also from
1801 // the compiler local RPN array.
1803 (*pCode
)->DecRef(); // may be dead now
1804 pFacToken
->SetByte( nSepCount
- 1 );
1808 // For the remaining two arguments cases use the
1809 // compatibility function.
1810 pFacToken
->NewOpCode( ocWeeknumOOo
, FormulaToken::PrivateAccess());
1813 PutCode( pFacToken
);
1817 // standard handling of 1-parameter opcodes
1818 pFacToken
= mpToken
;
1820 if( nNumFmt
== SvNumFormatType::UNDEFINED
&& eOp
== ocNot
)
1821 nNumFmt
= SvNumFormatType::LOGICAL
;
1825 CheckSetForceArrayParameter( mpToken
, 0);
1829 SetError( FormulaError::PairExpected
);
1831 SetError( FormulaError::PairExpected
);
1832 else if ( pArr
->GetCodeError() == FormulaError::NONE
)
1834 pFacToken
->SetByte( 1 );
1837 FormulaToken
** pArg
= pCode
- 1;
1838 HandleIIOpCode(pFacToken
, &pArg
, 1);
1841 PutCode( pFacToken
);
1845 else if ((SC_OPCODE_START_2_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_2_PAR
)
1846 || eOp
== ocExternal
1851 || ( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
1852 || (!mbJumpCommandReorder
&& IsOpCodeJumpCommand(eOp
)))
1854 pFacToken
= mpToken
;
1855 OpCode eMyLastOp
= eOp
;
1857 bool bNoParam
= false;
1858 bool bBadName
= false;
1866 CheckSetForceArrayParameter( mpToken
, 0);
1870 else if (eMyLastOp
== ocBad
)
1872 // Just a bad name, not an unknown function, no parameters, no
1873 // closing expected.
1878 SetError( FormulaError::PairExpected
);
1879 sal_uInt32 nSepCount
= 0;
1882 bool bDoIICompute
= mbComputeII
;
1883 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1884 FormulaToken
*** pArgArray
= nullptr;
1887 pArgArray
= static_cast<FormulaToken
***>(alloca(sizeof(FormulaToken
**)*FORMULA_MAXPARAMSII
));
1889 bDoIICompute
= false;
1895 pArgArray
[nSepCount
-1] = pCode
- 1; // Add first argument
1897 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1900 CheckSetForceArrayParameter( mpToken
, nSepCount
);
1902 if (nSepCount
> FORMULA_MAXPARAMS
)
1903 SetError( FormulaError::CodeOverflow
);
1905 if (bDoIICompute
&& nSepCount
<= FORMULA_MAXPARAMSII
)
1906 pArgArray
[nSepCount
- 1] = pCode
- 1; // Add rest of the arguments
1909 HandleIIOpCode(pFacToken
, pArgArray
,
1910 std::min(nSepCount
, static_cast<sal_uInt32
>(FORMULA_MAXPARAMSII
)));
1914 ; // nothing, keep current token for return
1915 else if (eOp
!= ocClose
)
1916 SetError( FormulaError::PairExpected
);
1922 // Jumps are just normal functions for the FunctionAutoPilot tree view
1923 if (!mbJumpCommandReorder
&& pFacToken
->GetType() == svJump
)
1924 pFacToken
= new FormulaFAPToken( pFacToken
->GetOpCode(), nSepCount
, pFacToken
);
1926 pFacToken
->SetByte( nSepCount
);
1927 PutCode( pFacToken
);
1932 else if (IsOpCodeJumpCommand(eOp
))
1934 // the PC counters are -1
1935 pFacToken
= mpToken
;
1939 pFacToken
->GetJump()[ 0 ] = 3; // if, else, behind
1942 pFacToken
->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT
+ 1;
1946 pFacToken
->GetJump()[ 0 ] = 2; // if, behind
1949 SAL_WARN("formula.core","Jump OpCode: " << +eOp
);
1950 assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case");
1956 CheckSetForceArrayParameter( mpToken
, 0);
1960 SetError( FormulaError::PairExpected
);
1961 PutCode( pFacToken
);
1962 // During AutoCorrect (since pArr->GetCodeError() is
1963 // ignored) an unlimited ocIf would crash because
1964 // ScRawToken::Clone() allocates the JumpBuffer according to
1965 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1967 OpCode eFacOpCode
= pFacToken
->GetOpCode();
1974 nJumpMax
= FORMULA_MAXJUMPCOUNT
;
1981 // May happen only if PutCode(pFacToken) ran into overflow.
1983 assert(pc
== FORMULA_MAXTOKENS
&& pArr
->GetCodeError() != FormulaError::NONE
);
1987 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode
);
1988 assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
1990 short nJumpCount
= 0;
1991 while ( (nJumpCount
< (FORMULA_MAXJUMPCOUNT
- 1)) && (eOp
== ocSep
)
1992 && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1994 if ( ++nJumpCount
<= nJumpMax
)
1995 pFacToken
->GetJump()[nJumpCount
] = pc
-1;
1997 CheckSetForceArrayParameter( mpToken
, nJumpCount
- 1);
1999 // ocSep or ocClose terminate the subexpression
2003 SetError( FormulaError::PairExpected
);
2007 // always limit to nJumpMax, no arbitrary overwrites
2008 if ( ++nJumpCount
<= nJumpMax
)
2009 pFacToken
->GetJump()[ nJumpCount
] = pc
-1;
2010 eFacOpCode
= pFacToken
->GetOpCode();
2015 bLimitOk
= (nJumpCount
<= 3);
2018 bLimitOk
= (nJumpCount
< FORMULA_MAXJUMPCOUNT
);
2022 bLimitOk
= (nJumpCount
<= 2);
2025 // May happen only if PutCode(pFacToken) ran into overflow.
2026 // This may had resulted from a stacked token array and
2027 // error wasn't propagated so assert only the program
2030 assert(pc
== FORMULA_MAXTOKENS
);
2034 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode
);
2035 assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case");
2038 pFacToken
->GetJump()[ 0 ] = nJumpCount
;
2040 SetError( FormulaError::IllegalParameter
);
2043 else if ( eOp
== ocMissing
)
2048 else if ( eOp
== ocClose
)
2050 SetError( FormulaError::ParameterExpected
);
2052 else if ( eOp
== ocSep
)
2053 { // Subsequent ocSep
2054 SetError( FormulaError::ParameterExpected
);
2055 if ( bAutoCorrect
&& !pStack
)
2057 aCorrectedSymbol
.clear();
2061 else if ( mpToken
->IsExternalRef() )
2068 SetError( FormulaError::UnknownToken
);
2069 if ( bAutoCorrect
&& !pStack
)
2071 if ( eOp
== ocStop
)
2072 { // trailing operator w/o operand
2073 sal_Int32 nLen
= aCorrectedFormula
.getLength();
2075 aCorrectedFormula
= aCorrectedFormula
.copy( 0, nLen
- 1 );
2076 aCorrectedSymbol
.clear();
2084 void FormulaCompiler::RangeLine()
2087 while (mpToken
->GetOpCode() == ocRange
)
2089 FormulaToken
** pCode1
= pCode
- 1;
2090 FormulaTokenRef p
= mpToken
;
2093 FormulaToken
** pCode2
= pCode
- 1;
2094 if (!MergeRangeReference( pCode1
, pCode2
))
2099 void FormulaCompiler::IntersectionLine()
2102 while (mpToken
->GetOpCode() == ocIntersect
|| mpToken
->GetOpCode() == ocSpaces
)
2104 sal_uInt16 nCodeIndex
= maArrIterator
.GetIndex() - 1;
2105 FormulaToken
** pCode1
= pCode
- 1;
2106 FormulaTokenRef p
= mpToken
;
2109 FormulaToken
** pCode2
= pCode
- 1;
2110 if (p
->GetOpCode() == ocSpaces
)
2112 // Convert to intersection if both left and right are references or
2113 // functions (potentially returning references, if not then a space
2114 // or no space would be a syntax error anyway), not other operators
2115 // or operands. Else discard.
2116 if (isAdjacentOrGapRpnEnd( pc
, pCode
, pCode1
, pCode2
) && isIntersectable( pCode1
, pCode2
))
2118 FormulaTokenRef
pIntersect( new FormulaByteToken( ocIntersect
));
2119 // Replace ocSpaces with ocIntersect so that when switching
2120 // formula syntax the correct operator string is created.
2121 // coverity[freed_arg : FALSE] - FormulaTokenRef has a ref so ReplaceToken won't delete pIntersect
2122 pArr
->ReplaceToken( nCodeIndex
, pIntersect
.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY
);
2123 PutCode( pIntersect
);
2133 void FormulaCompiler::UnionLine()
2136 while (mpToken
->GetOpCode() == ocUnion
)
2138 FormulaTokenRef p
= mpToken
;
2145 void FormulaCompiler::UnaryLine()
2147 if( mpToken
->GetOpCode() == ocAdd
)
2149 else if (SC_OPCODE_START_UN_OP
<= mpToken
->GetOpCode() &&
2150 mpToken
->GetOpCode() < SC_OPCODE_STOP_UN_OP
)
2152 FormulaTokenRef p
= mpToken
;
2157 FormulaToken
** pArg
= pCode
- 1;
2158 HandleIIOpCode(p
.get(), &pArg
, 1);
2166 void FormulaCompiler::PostOpLine()
2169 while ( mpToken
->GetOpCode() == ocPercentSign
)
2170 { // this operator _follows_ its operand
2173 FormulaToken
** pArg
= pCode
- 1;
2174 HandleIIOpCode(mpToken
.get(), &pArg
, 1);
2181 void FormulaCompiler::PowLine()
2184 while (mpToken
->GetOpCode() == ocPow
)
2186 FormulaTokenRef p
= mpToken
;
2187 FormulaToken
** pArgArray
[2];
2189 pArgArray
[0] = pCode
- 1; // Add first argument
2194 pArgArray
[1] = pCode
- 1; // Add second argument
2195 HandleIIOpCode(p
.get(), pArgArray
, 2);
2201 void FormulaCompiler::MulDivLine()
2204 while (mpToken
->GetOpCode() == ocMul
|| mpToken
->GetOpCode() == ocDiv
)
2206 FormulaTokenRef p
= mpToken
;
2207 FormulaToken
** pArgArray
[2];
2209 pArgArray
[0] = pCode
- 1; // Add first argument
2214 pArgArray
[1] = pCode
- 1; // Add second argument
2215 HandleIIOpCode(p
.get(), pArgArray
, 2);
2221 void FormulaCompiler::AddSubLine()
2224 while (mpToken
->GetOpCode() == ocAdd
|| mpToken
->GetOpCode() == ocSub
)
2226 FormulaTokenRef p
= mpToken
;
2227 FormulaToken
** pArgArray
[2];
2229 pArgArray
[0] = pCode
- 1; // Add first argument
2234 pArgArray
[1] = pCode
- 1; // Add second argument
2235 HandleIIOpCode(p
.get(), pArgArray
, 2);
2241 void FormulaCompiler::ConcatLine()
2244 while (mpToken
->GetOpCode() == ocAmpersand
)
2246 FormulaTokenRef p
= mpToken
;
2247 FormulaToken
** pArgArray
[2];
2249 pArgArray
[0] = pCode
- 1; // Add first argument
2254 pArgArray
[1] = pCode
- 1; // Add second argument
2255 HandleIIOpCode(p
.get(), pArgArray
, 2);
2261 void FormulaCompiler::CompareLine()
2264 while (mpToken
->GetOpCode() >= ocEqual
&& mpToken
->GetOpCode() <= ocGreaterEqual
)
2266 FormulaTokenRef p
= mpToken
;
2267 FormulaToken
** pArgArray
[2];
2269 pArgArray
[0] = pCode
- 1; // Add first argument
2274 pArgArray
[1] = pCode
- 1; // Add second argument
2275 HandleIIOpCode(p
.get(), pArgArray
, 2);
2281 OpCode
FormulaCompiler::Expression()
2283 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
2284 if ( nRecursion
> nRecursionMax
)
2286 SetError( FormulaError::StackOverflow
);
2287 return ocStop
; //! generate token instead?
2290 while (mpToken
->GetOpCode() == ocAnd
|| mpToken
->GetOpCode() == ocOr
)
2292 FormulaTokenRef p
= mpToken
;
2293 mpToken
->SetByte( 2 ); // 2 parameters!
2294 FormulaToken
** pArgArray
[2];
2296 pArgArray
[0] = pCode
- 1; // Add first argument
2301 pArgArray
[1] = pCode
- 1; // Add second argument
2302 HandleIIOpCode(p
.get(), pArgArray
, 2);
2306 return mpToken
->GetOpCode();
2310 void FormulaCompiler::SetError( FormulaError
/*nError*/ )
2314 FormulaTokenRef
FormulaCompiler::ExtendRangeReference( FormulaToken
& /*rTok1*/, FormulaToken
& /*rTok2*/ )
2316 return FormulaTokenRef();
2319 bool FormulaCompiler::MergeRangeReference( FormulaToken
* * const pCode1
, FormulaToken
* const * const pCode2
)
2321 if (!isAdjacentRpnEnd( pc
, pCode
, pCode1
, pCode2
))
2324 FormulaToken
*p1
= *pCode1
, *p2
= *pCode2
;
2325 FormulaTokenRef p
= ExtendRangeReference( *p1
, *p2
);
2339 bool FormulaCompiler::CompileTokenArray()
2343 needsRPNTokenCheck
= false;
2344 if (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
)
2348 aCorrectedFormula
.clear();
2349 aCorrectedSymbol
.clear();
2352 maArrIterator
.Reset();
2354 FormulaToken
* pDataArray
[ FORMULA_MAXTOKENS
+ 1 ];
2355 // Code in some places refers to the last token as 'pCode - 1', which may
2356 // point before the first element if the expression is bad. So insert a dummy
2357 // node in that place which will make that token be nullptr.
2358 pDataArray
[ 0 ] = nullptr;
2359 FormulaToken
** pData
= pDataArray
+ 1;
2361 bool bWasForced
= pArr
->IsRecalcModeForced();
2362 if ( bWasForced
&& bAutoCorrect
)
2363 aCorrectedFormula
= "=";
2364 pArr
->ClearRecalcMode();
2365 maArrIterator
.Reset();
2369 OpCode eOp
= Expression();
2370 // Some trailing garbage that doesn't form an expression?
2372 SetError( FormulaError::OperatorExpected
);
2375 FormulaError nErrorBeforePop
= pArr
->GetCodeError();
2381 pArr
->CreateNewRPNArrayFromData( pData
, pc
);
2382 if( needsRPNTokenCheck
)
2383 pArr
->CheckAllRPNTokens();
2386 // once an error, always an error
2387 if( pArr
->GetCodeError() == FormulaError::NONE
&& nErrorBeforePop
!= FormulaError::NONE
)
2388 pArr
->SetCodeError( nErrorBeforePop
);
2390 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
2393 maArrIterator
.Reset();
2394 pArr
->SetHyperLink( false);
2398 pArr
->SetRecalcModeForced();
2400 if( nNumFmt
== SvNumFormatType::UNDEFINED
)
2401 nNumFmt
= SvNumFormatType::NUMBER
;
2405 void FormulaCompiler::PopTokenArray()
2410 FormulaArrayStack
* p
= pStack
;
2412 // obtain special RecalcMode from SharedFormula
2413 if ( pArr
->IsRecalcModeAlways() )
2414 p
->pArr
->SetExclusiveRecalcModeAlways();
2415 else if ( !pArr
->IsRecalcModeNormal() && p
->pArr
->IsRecalcModeNormal() )
2416 p
->pArr
->SetMaskedRecalcMode( pArr
->GetRecalcMode() );
2417 p
->pArr
->SetCombinedBitsRecalcMode( pArr
->GetRecalcMode() );
2418 if ( pArr
->IsHyperLink() ) // fdo 87534
2419 p
->pArr
->SetHyperLink( true );
2423 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
2424 maArrIterator
.Jump(p
->nIndex
);
2425 mpLastToken
= p
->mpLastToken
;
2429 void FormulaCompiler::CreateStringFromTokenArray( OUString
& rFormula
)
2431 OUStringBuffer
aBuffer( pArr
->GetLen() * 5 );
2432 CreateStringFromTokenArray( aBuffer
);
2433 rFormula
= aBuffer
.makeStringAndClear();
2436 void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer
& rBuffer
)
2438 rBuffer
.setLength(0);
2439 if( !pArr
->GetLen() )
2442 FormulaTokenArray
* pSaveArr
= pArr
;
2443 int nSaveIndex
= maArrIterator
.GetIndex();
2444 bool bODFF
= FormulaGrammar::isODFF( meGrammar
);
2445 if (bODFF
|| FormulaGrammar::isPODF( meGrammar
) )
2447 // Scan token array for missing args and re-write if present.
2448 MissingConventionODF
aConv( bODFF
);
2449 if (pArr
->NeedsPodfRewrite( aConv
))
2451 pArr
= pArr
->RewriteMissing( aConv
);
2452 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2455 else if ( FormulaGrammar::isOOXML( meGrammar
) )
2457 // Scan token array for missing args and rewrite if present.
2458 if (pArr
->NeedsOoxmlRewrite())
2460 MissingConventionOOXML aConv
;
2461 pArr
= pArr
->RewriteMissing( aConv
);
2462 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2466 // At least one character per token, plus some are references, some are
2467 // function names, some are numbers, ...
2468 rBuffer
.ensureCapacity( pArr
->GetLen() * 5 );
2470 if ( pArr
->IsRecalcModeForced() )
2471 rBuffer
.append( '=');
2472 const FormulaToken
* t
= maArrIterator
.First();
2474 t
= CreateStringFromToken( rBuffer
, t
, true );
2476 if (pSaveArr
!= pArr
)
2480 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2481 maArrIterator
.Jump(nSaveIndex
);
2485 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUString
& rFormula
, const FormulaToken
* pTokenP
)
2487 OUStringBuffer aBuffer
;
2488 const FormulaToken
* p
= CreateStringFromToken( aBuffer
, pTokenP
);
2489 rFormula
+= aBuffer
;
2493 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUStringBuffer
& rBuffer
, const FormulaToken
* pTokenP
,
2494 bool bAllowArrAdvance
)
2497 bool bSpaces
= false;
2498 const FormulaToken
* t
= pTokenP
;
2499 OpCode eOp
= t
->GetOpCode();
2500 if( eOp
>= ocAnd
&& eOp
<= ocOr
)
2503 if ( bAllowArrAdvance
)
2504 t
= maArrIterator
.Next();
2506 t
= maArrIterator
.PeekNext();
2508 bSpaces
= ( !t
|| t
->GetOpCode() != ocOpen
);
2511 rBuffer
.append( ' ');
2513 if (eOp
== ocSpaces
|| eOp
== ocWhitespace
)
2515 bool bWriteSpaces
= true;
2516 if (eOp
== ocSpaces
&& mxSymbols
->isODFF())
2518 const FormulaToken
* p
= maArrIterator
.PeekPrevNoSpaces();
2519 bool bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
2520 if (bIntersectionOp
)
2522 p
= maArrIterator
.PeekNextNoSpaces();
2523 bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
2525 if (bIntersectionOp
)
2527 rBuffer
.append( "!!");
2528 bWriteSpaces
= false;
2533 // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
2534 // not separate a function name from its initial opening
2537 // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
2538 // shall separate a function-name from the left parenthesis (()
2539 // that follows it." and Excel even chokes on it.
2541 // Suppress/remove it in any case also in UI, it will not be
2543 const FormulaToken
* p
= maArrIterator
.PeekPrevNoSpaces();
2544 if (p
&& p
->IsFunction())
2546 p
= maArrIterator
.PeekNextNoSpaces();
2547 if (p
&& p
->GetOpCode() == ocOpen
)
2548 bWriteSpaces
= false;
2553 // most times it's just one blank
2554 sal_uInt8 n
= t
->GetByte();
2555 for ( sal_uInt8 j
=0; j
<n
; ++j
)
2557 if (eOp
== ocWhitespace
)
2558 rBuffer
.append( t
->GetChar());
2560 rBuffer
.append( ' ');
2564 else if( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
2565 rBuffer
.appendAscii( pInternal
[ eOp
- ocInternalBegin
] );
2566 else if (eOp
== ocIntersect
)
2568 // Nasty, ugly, horrific, terrifying...
2569 if (FormulaGrammar::isExcelSyntax( meGrammar
))
2570 rBuffer
.append(' ');
2572 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
2574 else if( static_cast<sal_uInt16
>(eOp
) < mxSymbols
->getSymbolCount()) // Keyword:
2575 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
2578 SAL_WARN( "formula.core","unknown OpCode");
2579 rBuffer
.append( GetNativeSymbol( ocErrName
));
2583 if (t
->IsExternalRef())
2585 CreateStringFromExternal( rBuffer
, pTokenP
);
2589 switch( t
->GetType() )
2592 AppendDouble( rBuffer
, t
->GetDouble() );
2596 if( eOp
== ocBad
|| eOp
== ocStringXML
)
2597 rBuffer
.append( t
->GetString().getString());
2599 AppendString( rBuffer
, t
->GetString().getString() );
2602 CreateStringFromSingleRef( rBuffer
, t
);
2605 CreateStringFromDoubleRef( rBuffer
, t
);
2609 CreateStringFromMatrix( rBuffer
, t
);
2613 CreateStringFromIndex( rBuffer
, t
);
2614 if (t
->GetOpCode() == ocTableRef
&& bAllowArrAdvance
&& NeedsTableRefTransformation())
2616 // Suppress all TableRef related tokens, the resulting
2617 // range was written by CreateStringFromIndex().
2618 const FormulaToken
* const p
= maArrIterator
.PeekNext();
2619 if (p
&& p
->GetOpCode() == ocTableRefOpen
)
2624 t
= maArrIterator
.Next();
2628 // Switch cases correspond with those in
2629 // ScCompiler::HandleTableRef()
2630 switch (t
->GetOpCode())
2632 case ocTableRefOpen
:
2635 case ocTableRefClose
:
2638 case ocTableRefItemAll
:
2639 case ocTableRefItemHeaders
:
2640 case ocTableRefItemData
:
2641 case ocTableRefItemTotals
:
2642 case ocTableRefItemThisRow
:
2659 // mapped or translated name of AddIns
2660 OUString
aAddIn( t
->GetExternal() );
2661 bool bMapped
= mxSymbols
->isPODF(); // ODF 1.1 directly uses programmatical name
2662 if (!bMapped
&& mxSymbols
->hasExternals())
2664 ExternalHashMap::const_iterator iLook
= mxSymbols
->getReverseExternalHashMap().find( aAddIn
);
2665 if (iLook
!= mxSymbols
->getReverseExternalHashMap().end())
2667 aAddIn
= (*iLook
).second
;
2671 if (!bMapped
&& !mxSymbols
->isEnglish())
2672 LocalizeString( aAddIn
);
2673 rBuffer
.append( aAddIn
);
2677 AppendErrorConstant( rBuffer
, t
->GetError());
2686 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t
->GetType());
2691 rBuffer
.append( ' ');
2692 if ( bAllowArrAdvance
)
2695 t
= maArrIterator
.Next();
2702 void FormulaCompiler::AppendDouble( OUStringBuffer
& rBuffer
, double fVal
) const
2704 if ( mxSymbols
->isEnglishLocale() )
2706 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
2707 rtl_math_StringFormat_Automatic
,
2708 rtl_math_DecimalPlaces_Max
, '.', true );
2712 SvtSysLocale aSysLocale
;
2713 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
2714 rtl_math_StringFormat_Automatic
,
2715 rtl_math_DecimalPlaces_Max
,
2716 aSysLocale
.GetLocaleData().getNumDecimalSep()[0],
2721 void FormulaCompiler::AppendBoolean( OUStringBuffer
& rBuffer
, bool bVal
) const
2723 rBuffer
.append( mxSymbols
->getSymbol( bVal
? ocTrue
: ocFalse
) );
2726 void FormulaCompiler::AppendString( OUStringBuffer
& rBuffer
, const OUString
& rStr
)
2728 rBuffer
.append( '"');
2729 if ( lcl_UnicodeStrChr( rStr
.getStr(), '"' ) == nullptr )
2730 rBuffer
.append( rStr
);
2733 OUString aStr
= rStr
.replaceAll( "\"", "\"\"" );
2734 rBuffer
.append(aStr
);
2736 rBuffer
.append( '"');
2739 bool FormulaCompiler::NeedsTableRefTransformation() const
2741 // Currently only UI representations and OOXML export use Table structured
2742 // references. Not defined in ODFF.
2743 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2744 // symbol is not defined there.
2745 return mxSymbols
->getSymbol( ocTableRefOpen
).isEmpty() || FormulaGrammar::isPODF( meGrammar
);
2748 void FormulaCompiler::UpdateSeparatorsNative(
2749 const OUString
& rSep
, const OUString
& rArrayColSep
, const OUString
& rArrayRowSep
)
2751 NonConstOpCodeMapPtr xSymbolsNative
;
2752 lcl_fillNativeSymbols( xSymbolsNative
);
2753 xSymbolsNative
->putOpCode( rSep
, ocSep
, nullptr);
2754 xSymbolsNative
->putOpCode( rArrayColSep
, ocArrayColSep
, nullptr);
2755 xSymbolsNative
->putOpCode( rArrayRowSep
, ocArrayRowSep
, nullptr);
2758 void FormulaCompiler::ResetNativeSymbols()
2760 NonConstOpCodeMapPtr xSymbolsNative
;
2761 lcl_fillNativeSymbols( xSymbolsNative
, InitSymbols::DESTROY
);
2762 lcl_fillNativeSymbols( xSymbolsNative
);
2765 void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr
& xMap
)
2767 NonConstOpCodeMapPtr xSymbolsNative
;
2768 lcl_fillNativeSymbols( xSymbolsNative
);
2769 xSymbolsNative
->copyFrom( *xMap
);
2773 OpCode
FormulaCompiler::NextToken()
2777 OpCode eOp
= mpToken
->GetOpCode();
2778 // There must be an operator before a push
2779 if ( (eOp
== ocPush
|| eOp
== ocColRowNameAuto
) &&
2780 !( (eLastOp
== ocOpen
) || (eLastOp
== ocSep
) ||
2781 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)) )
2782 SetError( FormulaError::OperatorExpected
);
2783 // Operator and Plus => operator
2784 if (eOp
== ocAdd
&& (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2785 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2787 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
2792 // Before an operator there must not be another operator, with the
2793 // exception of AND and OR.
2794 if ( eOp
!= ocAnd
&& eOp
!= ocOr
&&
2795 (SC_OPCODE_START_BIN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_BIN_OP
)
2796 && (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2797 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2799 SetError( FormulaError::VariableExpected
);
2800 if ( bAutoCorrect
&& !pStack
)
2802 if ( eOp
== eLastOp
|| eLastOp
== ocOpen
)
2803 { // throw away duplicated operator
2804 aCorrectedSymbol
.clear();
2809 sal_Int32 nPos
= aCorrectedFormula
.getLength();
2813 sal_Unicode c
= aCorrectedFormula
[ nPos
];
2817 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2818 { // >= instead of =>
2819 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2820 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocGreater
) ) );
2821 aCorrectedSymbol
= OUString(c
);
2826 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2827 { // <= instead of =<
2828 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2829 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocLess
) ) );
2830 aCorrectedSymbol
= OUString(c
);
2833 else if ( c
== mxSymbols
->getSymbolChar( ocGreater
) )
2834 { // <> instead of ><
2835 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2836 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocLess
) ) );
2837 aCorrectedSymbol
= OUString(c
);
2842 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2843 { // *- instead of -*
2844 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2845 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocMul
) ) );
2846 aCorrectedSymbol
= OUString(c
);
2851 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2852 { // /- instead of -/
2853 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2854 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocDiv
) ) );
2855 aCorrectedSymbol
= OUString(c
);
2866 // Nasty, ugly, horrific, terrifying... significant whitespace...
2867 if (eOp
== ocSpaces
&& FormulaGrammar::isExcelSyntax( meGrammar
))
2869 // Fake an intersection op as last op for the next round, but at
2870 // least roughly check if it could make sense at all.
2871 FormulaToken
* pPrev
= maArrIterator
.PeekPrevNoSpaces();
2872 if (pPrev
&& isPotentialRangeType( pPrev
, false, false))
2874 FormulaToken
* pNext
= maArrIterator
.PeekNextNoSpaces();
2875 if (pNext
&& isPotentialRangeType( pNext
, false, true))
2876 eLastOp
= ocIntersect
;
2889 void FormulaCompiler::PutCode( FormulaTokenRef
& p
)
2891 if( pc
>= FORMULA_MAXTOKENS
- 1 )
2893 if ( pc
== FORMULA_MAXTOKENS
- 1 )
2895 SAL_WARN("formula.core", "FormulaCompiler::PutCode - CodeOverflow with OpCode " << +p
->GetOpCode());
2896 p
= new FormulaByteToken( ocStop
);
2901 SetError( FormulaError::CodeOverflow
);
2904 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbJumpCommandReorder
)
2906 ForceArrayOperator( p
);
2913 bool FormulaCompiler::HandleExternalReference( const FormulaToken
& /*_aToken*/)
2918 bool FormulaCompiler::HandleRange()
2923 bool FormulaCompiler::HandleColRowName()
2928 bool FormulaCompiler::HandleDbData()
2933 bool FormulaCompiler::HandleTableRef()
2938 void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2942 void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2946 void FormulaCompiler::CreateStringFromIndex( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2950 void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2954 void FormulaCompiler::CreateStringFromExternal( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2958 void FormulaCompiler::LocalizeString( OUString
& /*rName*/ ) const
2962 formula::ParamClass
FormulaCompiler::GetForceArrayParameter( const FormulaToken
* /*pToken*/, sal_uInt16
/*nParam*/ ) const
2964 return ParamClass::Unknown
;
2967 void FormulaCompiler::ForceArrayOperator( FormulaTokenRef
const & rCurr
)
2969 if (pCurrentFactorToken
.get() == rCurr
.get())
2972 const OpCode eOp
= rCurr
->GetOpCode();
2973 const StackVar eType
= rCurr
->GetType();
2974 const bool bInlineArray
= (eOp
== ocPush
&& eType
== svMatrix
);
2978 if (rCurr
->GetInForceArray() != ParamClass::Unknown
)
2979 // Already set, unnecessary to evaluate again. This happens by calls to
2980 // CurrentFactor::operator=() while descending through Factor() and
2981 // then ascending back (and down and up, ...),
2982 // CheckSetForceArrayParameter() and later PutCode().
2985 if (!(eOp
!= ocPush
&& (eType
== svByte
|| eType
== svJump
)))
2989 // Return class for inline arrays and functions returning array/matrix.
2990 // It's somewhat unclear what Excel actually does there and in
2991 // ECMA-376-1:2016 OOXML mentions "call to ... shall be an array formula"
2992 // only for FREQUENCY() and TRANSPOSE() but not for any other function
2993 // returning array/matrix or inline arrays, though for the latter has one
2994 // example in 18.17.2 Syntax:
2995 // "SUM(SQRT({1,2,3,4})) returns 6.14 when entered normally". However,
2996 // these need to be treated similar but not as ParamClass::ForceArray
2997 // (which would contradict the example in
2998 // https://bugs.documentfoundation.org/show_bug.cgi?id=122301#c19 and A6 of
2999 // https://bugs.documentfoundation.org/show_bug.cgi?id=133260#c10 ).
3001 // commit d0ded163d8e93dc5b10d7a7c9bdab1d0a6a50bac
3002 // commit 5413c8871dec08eff19f514f5f391b946a45c86c
3003 constexpr ParamClass eArrayReturn
= ParamClass::ForceArrayReturn
;
3007 // rCurr->SetInForceArray() can not be used with ocPush, but ocPush
3008 // with svMatrix has an implicit ParamClass::ForceArrayReturn.
3009 if (nCurrentFactorParam
> 0 && pCurrentFactorToken
3010 && pCurrentFactorToken
->GetInForceArray() == ParamClass::Unknown
3011 && GetForceArrayParameter( pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1))
3012 == ParamClass::Value
)
3014 // Propagate to caller as if a function returning an array/matrix
3015 // was called (see also below).
3016 pCurrentFactorToken
->SetInForceArray( eArrayReturn
);
3021 if (!pCurrentFactorToken
)
3025 // An array/matrix formula acts as ForceArray on all top level
3026 // operators and function calls, so that can be inherited properly
3028 rCurr
->SetInForceArray( ParamClass::ForceArray
);
3030 else if (pc
>= 2 && SC_OPCODE_START_BIN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_BIN_OP
)
3032 // Binary operators are not functions followed by arguments
3033 // and need some peeking into RPN to inspect their operands.
3034 // Note that array context is not forced if only one
3035 // of the operands is an array like "={1;2}+A1:A2" returns #VALUE!
3036 // if entered in column A and not input in array mode, because it
3037 // involves a range reference with an implicit intersection. Check
3038 // both arguments are arrays, or the other is ocPush without ranges
3039 // for "={1;2}+3" or "={1;2}+A1".
3040 // Note this does not catch "={1;2}+ABS(A1)" that could be forced
3041 // to array, user still has to close in array mode.
3042 // The IsMatrixFunction() is only necessary because not all
3043 // functions returning matrix have ForceArrayReturn (yet?), see
3044 // OOXML comment above.
3046 const OpCode eOp1
= pCode
[-1]->GetOpCode();
3047 const OpCode eOp2
= pCode
[-2]->GetOpCode();
3048 const bool b1
= (pCode
[-1]->GetInForceArray() != ParamClass::Unknown
|| IsMatrixFunction(eOp1
));
3049 const bool b2
= (pCode
[-2]->GetInForceArray() != ParamClass::Unknown
|| IsMatrixFunction(eOp2
));
3051 || (b1
&& eOp2
== ocPush
&& pCode
[-2]->GetType() != svDoubleRef
)
3052 || (b2
&& eOp1
== ocPush
&& pCode
[-1]->GetType() != svDoubleRef
))
3054 rCurr
->SetInForceArray( eArrayReturn
);
3057 else if (pc
>= 1 && SC_OPCODE_START_UN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_UN_OP
)
3059 // Similar for unary operators.
3060 if (pCode
[-1]->GetInForceArray() != ParamClass::Unknown
|| IsMatrixFunction(pCode
[-1]->GetOpCode()))
3062 rCurr
->SetInForceArray( eArrayReturn
);
3068 // Inherited parameter class.
3069 const formula::ParamClass eForceType
= pCurrentFactorToken
->GetInForceArray();
3070 if (eForceType
== ParamClass::ForceArray
|| eForceType
== ParamClass::ReferenceOrRefArray
)
3072 // ReferenceOrRefArray was set only if in ForceArray context already,
3073 // it is valid for the one function only to indicate the preferred
3074 // return type. Propagate as ForceArray if not another parameter
3075 // handling ReferenceOrRefArray.
3076 if (nCurrentFactorParam
> 0
3077 && (GetForceArrayParameter( pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1))
3078 == ParamClass::ReferenceOrRefArray
))
3079 rCurr
->SetInForceArray( ParamClass::ReferenceOrRefArray
);
3081 rCurr
->SetInForceArray( ParamClass::ForceArray
);
3084 else if (eForceType
== ParamClass::ReferenceOrForceArray
)
3086 // Inherit further only if the return class of the nested function is
3087 // not Reference. Else flag as suppressed.
3088 if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) != ParamClass::Reference
)
3089 rCurr
->SetInForceArray( eForceType
);
3091 rCurr
->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray
);
3095 if (nCurrentFactorParam
<= 0)
3098 // Actual current parameter's class.
3099 const formula::ParamClass eParamType
= GetForceArrayParameter(
3100 pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1));
3101 if (eParamType
== ParamClass::ForceArray
)
3102 rCurr
->SetInForceArray( eParamType
);
3103 else if (eParamType
== ParamClass::ReferenceOrForceArray
)
3105 if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) != ParamClass::Reference
)
3106 rCurr
->SetInForceArray( eParamType
);
3108 rCurr
->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray
);
3111 // Propagate a ForceArrayReturn to caller if the called function
3112 // returns one and the caller so far does not have a stronger array
3113 // mode set and expects a scalar value for this parameter.
3114 if (eParamType
== ParamClass::Value
&& pCurrentFactorToken
->GetInForceArray() == ParamClass::Unknown
)
3116 if (IsMatrixFunction( eOp
))
3117 pCurrentFactorToken
->SetInForceArray( eArrayReturn
);
3118 else if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) == ParamClass::ForceArrayReturn
)
3119 pCurrentFactorToken
->SetInForceArray( ParamClass::ForceArrayReturn
);
3123 void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef
const & rCurr
, sal_uInt8 nParam
)
3125 if (!pCurrentFactorToken
)
3128 nCurrentFactorParam
= nParam
+ 1;
3130 ForceArrayOperator( rCurr
);
3133 void FormulaCompiler::PushTokenArray( FormulaTokenArray
* pa
, bool bTemp
)
3135 if ( bAutoCorrect
&& !pStack
)
3136 { // don't merge stacked subroutine code into entered formula
3137 aCorrectedFormula
+= aCorrectedSymbol
;
3138 aCorrectedSymbol
.clear();
3140 FormulaArrayStack
* p
= new FormulaArrayStack
;
3143 p
->nIndex
= maArrIterator
.GetIndex();
3144 p
->mpLastToken
= mpLastToken
;
3148 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
3151 } // namespace formula
3153 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */