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/processfactory.hxx>
35 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
36 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
37 #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
43 using namespace ::com::sun::star
;
45 static const char* pInternal
[2] = { "TTT", "__DEBUG_VAR" };
49 class FormulaCompilerRecursionGuard
54 explicit FormulaCompilerRecursionGuard( short& rRec
)
55 : rRecursion( rRec
) { ++rRecursion
; }
56 ~FormulaCompilerRecursionGuard() { --rRecursion
; }
59 SvNumFormatType
lcl_GetRetFormat( OpCode eOpCode
)
88 return SvNumFormatType::LOGICAL
;
92 return SvNumFormatType::DATE
;
94 return SvNumFormatType::DATETIME
;
96 return SvNumFormatType::TIME
;
110 return SvNumFormatType::CURRENCY
;
118 return SvNumFormatType::PERCENT
;
120 return SvNumFormatType::NUMBER
;
124 void lclPushOpCodeMapEntry( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
125 const OUString
* pTable
, sal_uInt16 nOpCode
)
127 sheet::FormulaOpCodeMapEntry aEntry
;
128 aEntry
.Token
.OpCode
= nOpCode
;
129 aEntry
.Name
= pTable
[nOpCode
];
130 rVec
.push_back( aEntry
);
133 void lclPushOpCodeMapEntries( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
134 const OUString
* pTable
, sal_uInt16 nOpCodeBeg
, sal_uInt16 nOpCodeEnd
)
136 for (sal_uInt16 nOpCode
= nOpCodeBeg
; nOpCode
< nOpCodeEnd
; ++nOpCode
)
137 lclPushOpCodeMapEntry( rVec
, pTable
, nOpCode
);
140 void lclPushOpCodeMapEntries( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
141 const OUString
* pTable
, const sal_uInt16
* pnOpCodes
, size_t nCount
)
143 for (const sal_uInt16
* pnEnd
= pnOpCodes
+ nCount
; pnOpCodes
< pnEnd
; ++pnOpCodes
)
144 lclPushOpCodeMapEntry( rVec
, pTable
, *pnOpCodes
);
147 CharClass
* createCharClassIfNonEnglishUI()
149 const LanguageTag
& rLanguageTag( Application::GetSettings().GetUILanguageTag());
150 if (rLanguageTag
.getLanguage() == "en")
152 return new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag
);
159 OpCodeList(const std::pair
<const char*, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
&,
160 FormulaCompiler::SeparatorType
= FormulaCompiler::SeparatorType::SEMICOLON_BASE
);
161 OpCodeList(const std::pair
<TranslateId
, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
&,
162 FormulaCompiler::SeparatorType
= FormulaCompiler::SeparatorType::SEMICOLON_BASE
);
165 bool getOpCodeString( OUString
& rStr
, sal_uInt16 nOp
);
166 void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, sal_uInt16 nOp
, const CharClass
* pCharClass
);
169 FormulaCompiler::SeparatorType meSepType
;
170 const std::pair
<const char*, int>* mpSymbols1
;
171 const std::pair
<TranslateId
, int>* mpSymbols2
;
174 OpCodeList::OpCodeList(const std::pair
<const char*, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
,
175 FormulaCompiler::SeparatorType eSepType
)
176 : meSepType(eSepType
)
177 , mpSymbols1(pSymbols
)
178 , mpSymbols2(nullptr)
180 std::unique_ptr
<CharClass
> xCharClass( xMap
->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
181 const CharClass
* pCharClass
= xCharClass
.get();
182 if (meSepType
== FormulaCompiler::SeparatorType::RESOURCE_BASE
)
184 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
186 putDefaultOpCode( xMap
, i
, pCharClass
);
191 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
194 if ( getOpCodeString( aOpStr
, i
) )
195 xMap
->putOpCode( aOpStr
, OpCode(i
), pCharClass
);
197 putDefaultOpCode( xMap
, i
, pCharClass
);
202 OpCodeList::OpCodeList(const std::pair
<TranslateId
, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
,
203 FormulaCompiler::SeparatorType eSepType
)
204 : meSepType(eSepType
)
205 , mpSymbols1(nullptr)
206 , mpSymbols2(pSymbols
)
208 std::unique_ptr
<CharClass
> xCharClass( xMap
->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
209 const CharClass
* pCharClass
= xCharClass
.get();
210 if (meSepType
== FormulaCompiler::SeparatorType::RESOURCE_BASE
)
212 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
214 putDefaultOpCode( xMap
, i
, pCharClass
);
219 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
222 if ( getOpCodeString( aOpStr
, i
) )
223 xMap
->putOpCode( aOpStr
, OpCode(i
), pCharClass
);
225 putDefaultOpCode( xMap
, i
, pCharClass
);
230 bool OpCodeList::getOpCodeString( OUString
& rStr
, sal_uInt16 nOp
)
236 if (meSepType
== FormulaCompiler::SeparatorType::SEMICOLON_BASE
)
243 case SC_OPCODE_ARRAY_COL_SEP
:
245 if (meSepType
== FormulaCompiler::SeparatorType::SEMICOLON_BASE
)
252 case SC_OPCODE_ARRAY_ROW_SEP
:
254 if (meSepType
== FormulaCompiler::SeparatorType::SEMICOLON_BASE
)
266 void OpCodeList::putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, sal_uInt16 nOp
,
267 const CharClass
* pCharClass
)
272 const char* pKey
= nullptr;
273 for (const std::pair
<const char*, int>* pSymbol
= mpSymbols1
; pSymbol
->first
; ++pSymbol
)
275 if (nOp
== pSymbol
->second
)
277 pKey
= pSymbol
->first
;
283 sKey
= OUString::createFromAscii(pKey
);
288 for (const std::pair
<TranslateId
, int>* pSymbol
= mpSymbols2
; pSymbol
->first
; ++pSymbol
)
290 if (nOp
== pSymbol
->second
)
292 pKey
= pSymbol
->first
;
298 sKey
= ForResId(pKey
);
300 xMap
->putOpCode(sKey
, OpCode(nOp
), pCharClass
);
304 const sal_Unicode
* lcl_UnicodeStrChr( const sal_Unicode
* pStr
, sal_Unicode c
)
319 FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap
;
324 bool isPotentialRangeLeftOp( OpCode eOp
)
335 bool isRangeResultFunction( OpCode eOp
)
347 bool isRangeResultOpCode( OpCode eOp
)
364 MUST be a valid token, caller has to ensure.
367 If bRPN==false, bRight==false means opcodes for left side are
368 checked, bRight==true means opcodes for right side. If bRPN==true
369 it doesn't matter except for the ocSep converted to ocUnion case.
371 bool isPotentialRangeType( FormulaToken
const * pToken
, bool bRPN
, bool bRight
)
373 switch (pToken
->GetType())
375 case svByte
: // could be range result, but only a few
377 return isRangeResultOpCode( pToken
->GetOpCode());
379 return isRangeResultFunction( pToken
->GetOpCode());
381 return isPotentialRangeLeftOp( pToken
->GetOpCode());
384 case svIndex
: // could be range
385 //case svRefList: // um..what?
386 case svExternalSingleRef
:
387 case svExternalDoubleRef
:
388 case svExternalName
: // could be range
391 // A special case if a previous ocSep was converted to ocUnion it
392 // stays svSep instead of svByte.
393 return bRPN
&& !bRight
&& pToken
->GetOpCode() == ocUnion
;
395 // Separators are not part of RPN and right opcodes need to be
396 // other StackVar types or functions and thus svByte.
397 return !bRPN
&& !bRight
&& isPotentialRangeLeftOp( pToken
->GetOpCode());
401 bool isIntersectable( FormulaToken
** pCode1
, FormulaToken
** pCode2
)
403 FormulaToken
* pToken1
= *pCode1
;
404 FormulaToken
* pToken2
= *pCode2
;
405 if (pToken1
&& pToken2
)
406 return isPotentialRangeType( pToken1
, true, false) && isPotentialRangeType( pToken2
, true, true);
410 bool isAdjacentRpnEnd( sal_uInt16 nPC
,
411 FormulaToken
const * const * const pCode
,
412 FormulaToken
const * const * const pCode1
,
413 FormulaToken
const * const * const pCode2
)
415 return nPC
>= 2 && pCode1
&& pCode2
&&
416 (pCode2
- pCode1
== 1) && (pCode
- pCode2
== 1) &&
417 (*pCode1
!= nullptr) && (*pCode2
!= nullptr);
420 bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC
,
421 FormulaToken
const * const * const pCode
,
422 FormulaToken
const * const * const pCode1
,
423 FormulaToken
const * const * const pCode2
)
425 return nPC
>= 2 && pCode1
&& pCode2
&&
426 (pCode2
> pCode1
) && (pCode
- pCode2
== 1) &&
427 (*pCode1
!= nullptr) && (*pCode2
!= nullptr);
434 void FormulaCompiler::OpCodeMap::putExternal( const OUString
& rSymbol
, const OUString
& rAddIn
)
436 // Different symbols may map to the same AddIn, but the same AddIn may not
437 // map to different symbols, the first pair wins. Same symbol of course may
438 // not map to different AddIns, again the first pair wins and also the
439 // AddIn->symbol mapping is not inserted in other cases.
440 bool bOk
= maExternalHashMap
.emplace(rSymbol
, rAddIn
).second
;
441 SAL_WARN_IF( !bOk
, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol
<< " -> " << rAddIn
);
444 bOk
= maReverseExternalHashMap
.emplace(rAddIn
, rSymbol
).second
;
445 // Failed insertion of the AddIn is ok for different symbols mapping to
446 // the same AddIn. Make this INFO only.
447 SAL_INFO_IF( !bOk
, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn
<< " -> " << rSymbol
);
451 void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString
& rSymbol
, const OUString
& rAddIn
)
453 // Same as putExternal() but no warning, instead info whether inserted or not.
454 bool bOk
= maExternalHashMap
.emplace(rSymbol
, rAddIn
).second
;
455 SAL_INFO( "formula.core", "OpCodeMap::putExternalSoftly: symbol " << (bOk
? "" : "not ") << "inserted, " << rSymbol
<< " -> " << rAddIn
);
458 bOk
= maReverseExternalHashMap
.emplace(rAddIn
, rSymbol
).second
;
459 SAL_INFO_IF( !bOk
, "formula.core", "OpCodeMap::putExternalSoftly: AddIn not inserted, " << rAddIn
<< " -> " << rSymbol
);
463 uno::Sequence
< sheet::FormulaToken
> FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
464 const FormulaCompiler
& rCompiler
, const uno::Sequence
< OUString
>& rNames
) const
466 const sal_Int32 nLen
= rNames
.getLength();
467 uno::Sequence
< sheet::FormulaToken
> aTokens( nLen
);
468 sheet::FormulaToken
* pToken
= aTokens
.getArray();
469 OUString
const * pName
= rNames
.getConstArray();
470 OUString
const * const pStop
= pName
+ nLen
;
471 for ( ; pName
< pStop
; ++pName
, ++pToken
)
473 OpCodeHashMap::const_iterator
iLook( maHashMap
.find( *pName
));
474 if (iLook
!= maHashMap
.end())
475 pToken
->OpCode
= (*iLook
).second
;
481 ExternalHashMap::const_iterator
iExt( maExternalHashMap
.find( *pName
));
482 if (iExt
!= maExternalHashMap
.end())
483 aIntName
= (*iExt
).second
;
484 // Check for existence not needed here, only name-mapping is of
487 if (aIntName
.isEmpty())
488 aIntName
= rCompiler
.FindAddInFunction(*pName
, !isEnglish()); // bLocalFirst=false for english
489 if (aIntName
.isEmpty())
490 pToken
->OpCode
= getOpCodeUnknown();
493 pToken
->OpCode
= ocExternal
;
494 pToken
->Data
<<= aIntName
;
501 uno::Sequence
< sheet::FormulaOpCodeMapEntry
> FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
502 const FormulaCompiler
& rCompiler
, const sal_Int32 nGroups
) const
504 using namespace sheet
;
506 // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
507 // we don't know in advance how many elements it will have we use a
508 // temporary vector to add elements and then copy to Sequence :-(
509 ::std::vector
< FormulaOpCodeMapEntry
> aVec
;
511 if (nGroups
== FormulaMapGroup::SPECIAL
)
513 // Use specific order, keep in sync with
514 // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
520 { FormulaMapGroupSpecialOffset::PUSH
, ocPush
} ,
521 { FormulaMapGroupSpecialOffset::CALL
, ocCall
} ,
522 { FormulaMapGroupSpecialOffset::STOP
, ocStop
} ,
523 { FormulaMapGroupSpecialOffset::EXTERNAL
, ocExternal
} ,
524 { FormulaMapGroupSpecialOffset::NAME
, ocName
} ,
525 { FormulaMapGroupSpecialOffset::NO_NAME
, ocNoName
} ,
526 { FormulaMapGroupSpecialOffset::MISSING
, ocMissing
} ,
527 { FormulaMapGroupSpecialOffset::BAD
, ocBad
} ,
528 { FormulaMapGroupSpecialOffset::SPACES
, ocSpaces
} ,
529 { FormulaMapGroupSpecialOffset::MAT_REF
, ocMatRef
} ,
530 { FormulaMapGroupSpecialOffset::DB_AREA
, ocDBArea
} ,
531 /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
532 { FormulaMapGroupSpecialOffset::MACRO
, ocMacro
} ,
533 { FormulaMapGroupSpecialOffset::COL_ROW_NAME
, ocColRowName
} ,
534 { FormulaMapGroupSpecialOffset::WHITESPACE
, ocWhitespace
}
536 const size_t nCount
= SAL_N_ELEMENTS(aMap
);
537 // Preallocate vector elements.
538 FormulaOpCodeMapEntry aEntry
;
539 aEntry
.Token
.OpCode
= getOpCodeUnknown();
540 aVec
.resize(nCount
, aEntry
);
544 size_t nIndex
= static_cast< size_t >( i
.nOff
);
545 if (aVec
.size() <= nIndex
)
547 // The offsets really should be aligned with the size, so if
548 // the vector was preallocated above this code to resize it is
549 // just a measure in case the table isn't in sync with the API,
550 // usually it isn't executed.
551 aEntry
.Token
.OpCode
= getOpCodeUnknown();
552 aVec
.resize( nIndex
+ 1, aEntry
);
554 aEntry
.Token
.OpCode
= i
.eOp
;
555 aVec
[nIndex
] = aEntry
;
560 /* FIXME: Once we support error constants in formulas we'll need a map
561 * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
562 * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
564 // Anything else but SPECIAL.
565 if ((nGroups
& FormulaMapGroup::SEPARATORS
) != 0)
567 static const sal_uInt16 aOpCodes
[] = {
572 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
574 if ((nGroups
& FormulaMapGroup::ARRAY_SEPARATORS
) != 0)
576 static const sal_uInt16 aOpCodes
[] = {
577 SC_OPCODE_ARRAY_OPEN
,
578 SC_OPCODE_ARRAY_CLOSE
,
579 SC_OPCODE_ARRAY_ROW_SEP
,
580 SC_OPCODE_ARRAY_COL_SEP
582 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
584 if ((nGroups
& FormulaMapGroup::UNARY_OPERATORS
) != 0)
586 // Due to the nature of the percent operator following its operand
587 // it isn't sorted into unary operators for compiler interna.
588 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), ocPercentSign
);
589 // "+" can be used as unary operator too, push only if binary group is not set
590 if ((nGroups
& FormulaMapGroup::BINARY_OPERATORS
) == 0)
591 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), ocAdd
);
592 // regular unary operators
593 for (sal_uInt16 nOp
= SC_OPCODE_START_UN_OP
; nOp
< SC_OPCODE_STOP_UN_OP
&& nOp
< mnSymbols
; ++nOp
)
595 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
598 if ((nGroups
& FormulaMapGroup::BINARY_OPERATORS
) != 0)
600 for (sal_uInt16 nOp
= SC_OPCODE_START_BIN_OP
; nOp
< SC_OPCODE_STOP_BIN_OP
&& nOp
< mnSymbols
; ++nOp
)
604 // AND and OR in fact are functions but for legacy reasons
605 // are sorted into binary operators for compiler interna.
610 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
614 if ((nGroups
& FormulaMapGroup::FUNCTIONS
) != 0)
616 // Function names are not consecutive, skip the gaps between
617 // functions with no parameter, functions with 1 parameter
618 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), SC_OPCODE_START_NO_PAR
,
619 ::std::min
< sal_uInt16
>( SC_OPCODE_STOP_NO_PAR
, mnSymbols
) );
620 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), SC_OPCODE_START_1_PAR
,
621 ::std::min
< sal_uInt16
>( SC_OPCODE_STOP_1_PAR
, mnSymbols
) );
622 // Additional functions not within range of functions.
623 static const sal_uInt16 aOpCodes
[] = {
631 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
632 // functions with 2 or more parameters.
633 for (sal_uInt16 nOp
= SC_OPCODE_START_2_PAR
; nOp
< SC_OPCODE_STOP_2_PAR
&& nOp
< mnSymbols
; ++nOp
)
637 // NO_NAME is in SPECIAL.
638 case SC_OPCODE_NO_NAME
:
641 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
644 // If AddIn functions are present in this mapping, use them, and only those.
647 for (auto const& elem
: maExternalHashMap
)
649 FormulaOpCodeMapEntry aEntry
;
650 aEntry
.Name
= elem
.first
;
651 aEntry
.Token
.Data
<<= elem
.second
;
652 aEntry
.Token
.OpCode
= ocExternal
;
653 aVec
.push_back( aEntry
);
658 rCompiler
.fillAddInToken( aVec
, isEnglish());
662 return uno::Sequence
< FormulaOpCodeMapEntry
>(aVec
.data(), aVec
.size());
666 void FormulaCompiler::OpCodeMap::putOpCode( const OUString
& rStr
, const OpCode eOp
, const CharClass
* pCharClass
)
668 if (0 < eOp
&& sal_uInt16(eOp
) < mnSymbols
)
670 bool bPutOp
= mpTable
[eOp
].isEmpty();
671 bool bRemoveFromMap
= false;
676 // These OpCodes are meant to overwrite and also remove an
680 bRemoveFromMap
= true;
682 // These separator OpCodes are meant to overwrite and also
683 // remove an existing mapping if it is not used for one of the
687 bRemoveFromMap
= (mpTable
[ocArrayRowSep
] != mpTable
[eOp
] && mpTable
[ocSep
] != mpTable
[eOp
]);
691 bRemoveFromMap
= (mpTable
[ocArrayColSep
] != mpTable
[eOp
] && mpTable
[ocSep
] != mpTable
[eOp
]);
693 // For ocSep keep the ";" in map but remove any other if it is
694 // not used for ocArrayColSep or ocArrayRowSep.
697 bRemoveFromMap
= (mpTable
[eOp
] != ";" &&
698 mpTable
[ocArrayColSep
] != mpTable
[eOp
] &&
699 mpTable
[ocArrayRowSep
] != mpTable
[eOp
]);
701 // These OpCodes are known to be duplicates in the Excel
702 // external API mapping because of different parameter counts
703 // in different BIFF versions. Names are identical and entries
714 case ocGetDiffDate360
:
715 if (rStr
== mpTable
[eOp
])
718 // These OpCodes are known to be added to an existing mapping,
719 // but only for the OOXML external API mapping. This is *not*
720 // FormulaLanguage::OOXML. Keep the first
721 // (correct) definition for the OpCode, all following are
722 // additional alias entries in the map.
730 FormulaGrammar::extractFormulaLanguage( meGrammar
) == FormulaGrammar::GRAM_EXTERNAL
)
732 // Both bPutOp and bRemoveFromMap stay false.
737 SAL_WARN("formula.core",
738 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16
>(eOp
)
739 << ", replacing '" << mpTable
[eOp
] << "' with '" << rStr
<< "' in "
740 << (mbEnglish
? "" : "non-") << "English map 0x" << ::std::hex
<< meGrammar
);
744 // Case preserving opcode -> string, upper string -> opcode
747 OUString
aUpper( pCharClass
? pCharClass
->uppercase( mpTable
[eOp
]) : rStr
.toAsciiUpperCase());
748 // Ensure we remove a mapping only for the requested OpCode.
749 OpCodeHashMap::const_iterator
it( maHashMap
.find( aUpper
));
750 if (it
!= maHashMap
.end() && (*it
).second
== eOp
)
751 maHashMap
.erase( it
);
755 OUString
aUpper( pCharClass
? pCharClass
->uppercase( rStr
) : rStr
.toAsciiUpperCase());
756 maHashMap
.emplace(aUpper
, eOp
);
760 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
765 FormulaCompiler::FormulaCompiler( FormulaTokenArray
& rArr
, bool bComputeII
, bool bMatrixFlag
)
767 nCurrentFactorParam(0),
769 maArrIterator( rArr
),
774 nNumFmt( SvNumFormatType::UNDEFINED
),
776 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
777 bAutoCorrect( false ),
780 needsRPNTokenCheck( false ),
781 mbJumpCommandReorder(true),
783 mbComputeII(bComputeII
),
784 mbMatrixFlag(bMatrixFlag
)
788 FormulaTokenArray
FormulaCompiler::smDummyTokenArray
;
790 FormulaCompiler::FormulaCompiler(bool bComputeII
, bool bMatrixFlag
)
792 nCurrentFactorParam(0),
794 maArrIterator( smDummyTokenArray
),
799 nNumFmt( SvNumFormatType::UNDEFINED
),
801 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
802 bAutoCorrect( false ),
805 needsRPNTokenCheck( false ),
806 mbJumpCommandReorder(true),
808 mbComputeII(bComputeII
),
809 mbMatrixFlag(bMatrixFlag
)
813 FormulaCompiler::~FormulaCompiler()
817 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage
) const
819 const bool bTemporary
= !HasOpCodeMap(nLanguage
);
820 OpCodeMapPtr xMap
= GetFinalOpCodeMap(nLanguage
);
822 const_cast<FormulaCompiler
*>(this)->DestroyOpCodeMap(nLanguage
);
826 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::GetFinalOpCodeMap( const sal_Int32 nLanguage
) const
828 FormulaCompiler::OpCodeMapPtr xMap
;
829 using namespace sheet
;
832 case FormulaLanguage::ODFF
:
834 InitSymbolsODFF( InitSymbols::INIT
);
835 xMap
= mxSymbolsODFF
;
837 case FormulaLanguage::ODF_11
:
839 InitSymbolsPODF( InitSymbols::INIT
);
840 xMap
= mxSymbolsPODF
;
842 case FormulaLanguage::ENGLISH
:
843 if (!mxSymbolsEnglish
)
844 InitSymbolsEnglish( InitSymbols::INIT
);
845 xMap
= mxSymbolsEnglish
;
847 case FormulaLanguage::NATIVE
:
848 if (!mxSymbolsNative
)
849 InitSymbolsNative( InitSymbols::INIT
);
850 xMap
= mxSymbolsNative
;
852 case FormulaLanguage::XL_ENGLISH
:
853 if (!mxSymbolsEnglishXL
)
854 InitSymbolsEnglishXL( InitSymbols::INIT
);
855 xMap
= mxSymbolsEnglishXL
;
857 case FormulaLanguage::OOXML
:
859 InitSymbolsOOXML( InitSymbols::INIT
);
860 xMap
= mxSymbolsOOXML
;
862 case FormulaLanguage::API
:
864 InitSymbolsAPI( InitSymbols::INIT
);
868 ; // nothing, NULL map returned
873 void FormulaCompiler::DestroyOpCodeMap( const sal_Int32 nLanguage
)
875 using namespace sheet
;
878 case FormulaLanguage::ODFF
:
879 InitSymbolsODFF( InitSymbols::DESTROY
);
881 case FormulaLanguage::ODF_11
:
882 InitSymbolsPODF( InitSymbols::DESTROY
);
884 case FormulaLanguage::ENGLISH
:
885 InitSymbolsEnglish( InitSymbols::DESTROY
);
887 case FormulaLanguage::NATIVE
:
888 InitSymbolsNative( InitSymbols::DESTROY
);
890 case FormulaLanguage::XL_ENGLISH
:
891 InitSymbolsEnglishXL( InitSymbols::DESTROY
);
893 case FormulaLanguage::OOXML
:
894 InitSymbolsOOXML( InitSymbols::DESTROY
);
896 case FormulaLanguage::API
:
897 InitSymbolsAPI( InitSymbols::DESTROY
);
904 bool FormulaCompiler::HasOpCodeMap( const sal_Int32 nLanguage
) const
906 using namespace sheet
;
909 case FormulaLanguage::ODFF
:
910 return InitSymbolsODFF( InitSymbols::ASK
);
911 case FormulaLanguage::ODF_11
:
912 return InitSymbolsPODF( InitSymbols::ASK
);
913 case FormulaLanguage::ENGLISH
:
914 return InitSymbolsEnglish( InitSymbols::ASK
);
915 case FormulaLanguage::NATIVE
:
916 return InitSymbolsNative( InitSymbols::ASK
);
917 case FormulaLanguage::XL_ENGLISH
:
918 return InitSymbolsEnglishXL( InitSymbols::ASK
);
919 case FormulaLanguage::OOXML
:
920 return InitSymbolsOOXML( InitSymbols::ASK
);
921 case FormulaLanguage::API
:
922 return InitSymbolsAPI( InitSymbols::ASK
);
929 OUString
FormulaCompiler::FindAddInFunction( const OUString
& /*rUpperName*/, bool /*bLocalFirst*/ ) const
934 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::CreateOpCodeMap(
936 const sheet::FormulaOpCodeMapEntry
> & rMapping
,
939 using sheet::FormulaOpCodeMapEntry
;
940 // Filter / API maps are never Core
941 NonConstOpCodeMapPtr xMap
= std::make_shared
<OpCodeMap
>( SC_OPCODE_LAST_OPCODE_ID
+ 1, false,
942 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
943 FormulaGrammar::GRAM_EXTERNAL
, bEnglish
), FormulaGrammar::CONV_UNSPECIFIED
));
944 std::unique_ptr
<CharClass
> xCharClass( xMap
->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
945 const CharClass
* pCharClass
= xCharClass
.get();
946 for (auto const& rMapEntry
: rMapping
)
948 OpCode eOp
= OpCode(rMapEntry
.Token
.OpCode
);
949 if (eOp
!= ocExternal
)
950 xMap
->putOpCode( rMapEntry
.Name
, eOp
, pCharClass
);
953 OUString aExternalName
;
954 if (rMapEntry
.Token
.Data
>>= aExternalName
)
955 xMap
->putExternal( rMapEntry
.Name
, aExternalName
);
958 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
965 static bool lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, FormulaCompiler::InitSymbols eWhat
= FormulaCompiler::InitSymbols::INIT
)
967 static OpCodeMapData aSymbolMap
;
968 std::unique_lock
aGuard(aSymbolMap
.maMtx
);
970 if (eWhat
== FormulaCompiler::InitSymbols::ASK
)
972 return bool(aSymbolMap
.mxSymbolMap
);
974 else if (eWhat
== FormulaCompiler::InitSymbols::DESTROY
)
976 aSymbolMap
.mxSymbolMap
.reset();
978 else if (!aSymbolMap
.mxSymbolMap
)
981 aSymbolMap
.mxSymbolMap
=
982 std::make_shared
<FormulaCompiler::OpCodeMap
>(
983 SC_OPCODE_LAST_OPCODE_ID
+ 1, true, FormulaGrammar::GRAM_NATIVE_UI
);
984 OpCodeList
aOpCodeListSymbols(RID_STRLIST_FUNCTION_NAMES_SYMBOLS
, aSymbolMap
.mxSymbolMap
);
985 OpCodeList
aOpCodeListNative(RID_STRLIST_FUNCTION_NAMES
, aSymbolMap
.mxSymbolMap
);
986 // No AddInMap for native core mapping.
989 xMap
= aSymbolMap
.mxSymbolMap
;
994 const OUString
& FormulaCompiler::GetNativeSymbol( OpCode eOp
)
996 NonConstOpCodeMapPtr xSymbolsNative
;
997 lcl_fillNativeSymbols( xSymbolsNative
);
998 return xSymbolsNative
->getSymbol( eOp
);
1001 sal_Unicode
FormulaCompiler::GetNativeSymbolChar( OpCode eOp
)
1003 return GetNativeSymbol(eOp
)[0];
1006 bool FormulaCompiler::InitSymbolsNative( FormulaCompiler::InitSymbols eWhat
) const
1008 return lcl_fillNativeSymbols( mxSymbolsNative
, eWhat
);
1011 bool FormulaCompiler::InitSymbolsEnglish( FormulaCompiler::InitSymbols eWhat
) const
1013 static OpCodeMapData aMap
;
1014 std::unique_lock
aGuard(aMap
.maMtx
);
1015 if (eWhat
== InitSymbols::ASK
)
1016 return bool(aMap
.mxSymbolMap
);
1017 else if (eWhat
== InitSymbols::DESTROY
)
1018 aMap
.mxSymbolMap
.reset();
1019 else if (!aMap
.mxSymbolMap
)
1020 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
1021 mxSymbolsEnglish
= aMap
.mxSymbolMap
;
1025 bool FormulaCompiler::InitSymbolsPODF( FormulaCompiler::InitSymbols eWhat
) const
1027 static OpCodeMapData aMap
;
1028 std::unique_lock
aGuard(aMap
.maMtx
);
1029 if (eWhat
== InitSymbols::ASK
)
1030 return bool(aMap
.mxSymbolMap
);
1031 else if (eWhat
== InitSymbols::DESTROY
)
1032 aMap
.mxSymbolMap
.reset();
1033 else if (!aMap
.mxSymbolMap
)
1034 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF
, FormulaGrammar::GRAM_PODF
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1035 mxSymbolsPODF
= aMap
.mxSymbolMap
;
1039 bool FormulaCompiler::InitSymbolsAPI( FormulaCompiler::InitSymbols eWhat
) const
1041 static OpCodeMapData aMap
;
1042 std::unique_lock
aGuard(aMap
.maMtx
);
1043 if (eWhat
== InitSymbols::ASK
)
1044 return bool(aMap
.mxSymbolMap
);
1045 else if (eWhat
== InitSymbols::DESTROY
)
1046 aMap
.mxSymbolMap
.reset();
1047 else if (!aMap
.mxSymbolMap
)
1048 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API
, FormulaGrammar::GRAM_API
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1049 mxSymbolsAPI
= aMap
.mxSymbolMap
;
1053 bool FormulaCompiler::InitSymbolsODFF( FormulaCompiler::InitSymbols eWhat
) const
1055 static OpCodeMapData aMap
;
1056 std::unique_lock
aGuard(aMap
.maMtx
);
1057 if (eWhat
== InitSymbols::ASK
)
1058 return bool(aMap
.mxSymbolMap
);
1059 else if (eWhat
== InitSymbols::DESTROY
)
1060 aMap
.mxSymbolMap
.reset();
1061 else if (!aMap
.mxSymbolMap
)
1062 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF
, FormulaGrammar::GRAM_ODFF
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1063 mxSymbolsODFF
= aMap
.mxSymbolMap
;
1067 bool FormulaCompiler::InitSymbolsEnglishXL( FormulaCompiler::InitSymbols eWhat
) const
1069 static OpCodeMapData aMap
;
1070 std::unique_lock
aGuard(aMap
.maMtx
);
1071 if (eWhat
== InitSymbols::ASK
)
1072 return bool(aMap
.mxSymbolMap
);
1073 else if (eWhat
== InitSymbols::DESTROY
)
1074 aMap
.mxSymbolMap
.reset();
1075 else if (!aMap
.mxSymbolMap
)
1076 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
1077 mxSymbolsEnglishXL
= aMap
.mxSymbolMap
;
1078 if (eWhat
!= InitSymbols::INIT
)
1081 // TODO: For now, just replace the separators to the Excel English
1082 // variants. Later, if we want to properly map Excel functions with Calc
1083 // functions, we'll need to do a little more work here.
1084 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocSep
, nullptr);
1085 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocArrayColSep
, nullptr);
1086 mxSymbolsEnglishXL
->putOpCode( OUString(';'), ocArrayRowSep
, nullptr);
1091 bool FormulaCompiler::InitSymbolsOOXML( FormulaCompiler::InitSymbols eWhat
) const
1093 static OpCodeMapData aMap
;
1094 std::unique_lock
aGuard(aMap
.maMtx
);
1095 if (eWhat
== InitSymbols::ASK
)
1096 return bool(aMap
.mxSymbolMap
);
1097 else if (eWhat
== InitSymbols::DESTROY
)
1098 aMap
.mxSymbolMap
.reset();
1099 else if (!aMap
.mxSymbolMap
)
1100 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML
, FormulaGrammar::GRAM_OOXML
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
1101 mxSymbolsOOXML
= aMap
.mxSymbolMap
;
1106 void FormulaCompiler::loadSymbols(const std::pair
<const char*, int>* pSymbols
, FormulaGrammar::Grammar eGrammar
,
1107 NonConstOpCodeMapPtr
& rxMap
, SeparatorType eSepType
) const
1113 rxMap
= std::make_shared
<OpCodeMap
>( SC_OPCODE_LAST_OPCODE_ID
+ 1, eGrammar
!= FormulaGrammar::GRAM_ODFF
, eGrammar
);
1114 OpCodeList
aOpCodeList(pSymbols
, rxMap
, eSepType
);
1116 fillFromAddInMap( rxMap
, eGrammar
);
1117 // Fill from collection for AddIns not already present.
1118 if (FormulaGrammar::GRAM_ENGLISH
== eGrammar
)
1119 fillFromAddInCollectionEnglishName( rxMap
);
1122 fillFromAddInCollectionUpperName( rxMap
);
1123 if (FormulaGrammar::GRAM_API
== eGrammar
)
1125 // Add known but not in AddInMap English names, e.g. from the
1126 // PricingFunctions AddIn or any user supplied AddIn.
1127 fillFromAddInCollectionEnglishName( rxMap
);
1132 void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr
& /*xMap */) const
1136 void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr
& /*xMap */) const
1140 void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr
& /*xMap*/, FormulaGrammar::Grammar
/*_eGrammar */) const
1144 OpCode
FormulaCompiler::GetEnglishOpCode( const OUString
& rName
) const
1146 FormulaCompiler::OpCodeMapPtr xMap
= GetOpCodeMap( sheet::FormulaLanguage::ENGLISH
);
1148 formula::OpCodeHashMap::const_iterator
iLook( xMap
->getHashMap().find( rName
) );
1149 bool bFound
= (iLook
!= xMap
->getHashMap().end());
1150 return bFound
? (*iLook
).second
: ocNone
;
1153 bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp
)
1165 // more than one parameters:
1166 // ocIndirect otherwise would have to do
1167 // StopListening and StartListening on a reference for every
1168 // interpreted value.
1170 // ocOffset results in indirect references.
1172 // ocDebugVar shows internal value that may change as the internal state changes.
1183 bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp
)
1198 // Remove quotes, escaped quotes are unescaped.
1199 bool FormulaCompiler::DeQuote( OUString
& rStr
)
1201 sal_Int32 nLen
= rStr
.getLength();
1202 if ( nLen
> 1 && rStr
[0] == '\'' && rStr
[ nLen
-1 ] == '\'' )
1204 rStr
= rStr
.copy( 1, nLen
-2 );
1205 rStr
= rStr
.replaceAll( "''", "'" );
1211 void FormulaCompiler::fillAddInToken(
1212 ::std::vector
< sheet::FormulaOpCodeMapEntry
>& /*_rVec*/,
1213 bool /*_bIsEnglish*/) const
1217 bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode
)
1231 case ocModalValue_Multi
:
1236 // added to avoid warnings
1243 void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString
& rSymbol
, OpCode eOp
)
1245 SAL_WARN_IF( !mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty(), "formula.core",
1246 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16
>(eOp
)
1247 << " '" << mpTable
[eOp
] << "' with empty name!");
1248 if (!mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty())
1249 maHashMap
.emplace(mpTable
[eOp
], eOp
);
1252 mpTable
[eOp
] = rSymbol
;
1253 maHashMap
.emplace(rSymbol
, eOp
);
1257 void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap
& r
)
1259 maHashMap
= OpCodeHashMap( mnSymbols
);
1261 sal_uInt16 n
= r
.getSymbolCount();
1262 SAL_WARN_IF( n
!= mnSymbols
, "formula.core",
1263 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols
<< " that: " << n
);
1267 // OpCode 0 (ocPush) should never be in a map.
1268 SAL_WARN_IF( !mpTable
[0].isEmpty() || !r
.mpTable
[0].isEmpty(), "formula.core",
1269 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
1270 << mpTable
[0] << "' that: '" << r
.mpTable
[0] << "'");
1272 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
1273 // and API) to the native map (UI "use English function names") replace the
1274 // known bad legacy function names with correct ones.
1276 FormulaGrammar::extractFormulaLanguage( meGrammar
) == sheet::FormulaLanguage::NATIVE
&&
1277 FormulaGrammar::extractFormulaLanguage( r
.meGrammar
) == sheet::FormulaLanguage::ENGLISH
)
1279 for (sal_uInt16 i
= 1; i
< n
; ++i
)
1282 OpCode eOp
= OpCode(i
);
1289 aSymbol
= "MULTIPLE.OPERATIONS";
1292 aSymbol
= r
.mpTable
[i
];
1294 putCopyOpCode( aSymbol
, eOp
);
1299 for (sal_uInt16 i
= 1; i
< n
; ++i
)
1301 OpCode eOp
= OpCode(i
);
1302 const OUString
& rSymbol
= r
.mpTable
[i
];
1303 putCopyOpCode( rSymbol
, eOp
);
1307 // This was meant to copy to native map that does not have AddIn symbols
1308 // but needs them from the source map. It is unclear what should happen if
1309 // the destination already had externals, so do it only if it doesn't.
1310 if (!hasExternals())
1312 maExternalHashMap
= r
.maExternalHashMap
;
1313 maReverseExternalHashMap
= r
.maReverseExternalHashMap
;
1315 if (mbEnglish
!= r
.mbEnglish
)
1317 // For now keep mbEnglishLocale setting, which is false for a
1318 // non-English native map we're copying to.
1320 if (!mbEnglish && r.mbEnglish)
1321 mbEnglishLocale = "getUseEnglishLocaleFromConfiguration()";
1322 or set from outside i.e. via ScCompiler.
1324 mbEnglish
= r
.mbEnglish
;
1330 FormulaError
FormulaCompiler::GetErrorConstant( const OUString
& rName
) const
1332 FormulaError nError
= FormulaError::NONE
;
1333 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
));
1334 if (iLook
!= mxSymbols
->getHashMap().end())
1336 switch ((*iLook
).second
)
1338 // Not all may make sense in a formula, but these we know as
1341 nError
= FormulaError::NoCode
;
1344 nError
= FormulaError::DivisionByZero
;
1347 nError
= FormulaError::NoValue
;
1350 nError
= FormulaError::NoRef
;
1353 nError
= FormulaError::NoName
;
1356 nError
= FormulaError::IllegalFPOperation
;
1359 nError
= FormulaError::NotAvailable
;
1367 // Per convention recognize detailed "#ERRxxx!" constants, always
1368 // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
1370 if (rName
.startsWithIgnoreAsciiCase("#ERR") && rName
.getLength() <= 10 && rName
[rName
.getLength()-1] == '!')
1372 sal_uInt32 nErr
= o3tl::toUInt32(rName
.subView( 4, rName
.getLength() - 5));
1373 if (0 < nErr
&& nErr
<= SAL_MAX_UINT16
&& isPublishedFormulaError(static_cast<FormulaError
>(nErr
)))
1374 nError
= static_cast<FormulaError
>(nErr
);
1380 void FormulaCompiler::EnableJumpCommandReorder( bool bEnable
)
1382 mbJumpCommandReorder
= bEnable
;
1385 void FormulaCompiler::EnableStopOnError( bool bEnable
)
1387 mbStopOnError
= bEnable
;
1390 void FormulaCompiler::AppendErrorConstant( OUStringBuffer
& rBuffer
, FormulaError nError
) const
1395 case FormulaError::NoCode
:
1398 case FormulaError::DivisionByZero
:
1401 case FormulaError::NoValue
:
1404 case FormulaError::NoRef
:
1407 case FormulaError::NoName
:
1410 case FormulaError::IllegalFPOperation
:
1413 case FormulaError::NotAvailable
:
1418 // Per convention create detailed "#ERRxxx!" constants, always
1420 rBuffer
.append("#ERR");
1421 rBuffer
.append(static_cast<sal_Int32
>(nError
));
1422 rBuffer
.append('!');
1426 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
1429 constexpr short nRecursionMax
= 100;
1431 bool FormulaCompiler::GetToken()
1433 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
1434 if ( nRecursion
> nRecursionMax
)
1436 SetError( FormulaError::StackOverflow
);
1437 mpLastToken
= mpToken
= new FormulaByteToken( ocStop
);
1440 if ( bAutoCorrect
&& !pStack
)
1441 { // don't merge stacked subroutine code into entered formula
1442 aCorrectedFormula
+= aCorrectedSymbol
;
1443 aCorrectedSymbol
.clear();
1446 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
1450 FormulaTokenRef pSpacesToken
;
1451 short nWasColRowName
;
1452 if ( pArr
->OpCodeBefore( maArrIterator
.GetIndex() ) == ocColRowName
)
1457 mpToken
= maArrIterator
.Next();
1458 while (mpToken
&& ((eTmpOp
= mpToken
->GetOpCode()) == ocSpaces
|| eTmpOp
== ocWhitespace
))
1460 if (eTmpOp
== ocSpaces
)
1462 // For significant whitespace remember last ocSpaces token.
1463 // Usually there's only one even for multiple spaces.
1464 pSpacesToken
= mpToken
;
1465 if ( nWasColRowName
)
1468 if ( bAutoCorrect
&& !pStack
)
1469 CreateStringFromToken( aCorrectedFormula
, mpToken
.get() );
1470 mpToken
= maArrIterator
.Next();
1472 if ( bAutoCorrect
&& !pStack
&& mpToken
)
1473 CreateStringFromToken( aCorrectedSymbol
, mpToken
.get() );
1479 // mpLastToken was popped as well and corresponds to the
1480 // then current last token during PushTokenArray(), e.g. for
1489 if ( nWasColRowName
>= 2 && mpToken
->GetOpCode() == ocColRowName
)
1490 { // convert an ocSpaces to ocIntersect in RPN
1491 mpLastToken
= mpToken
= new FormulaByteToken( ocIntersect
);
1492 maArrIterator
.StepBack(); // we advanced to the second ocColRowName, step back
1494 else if (pSpacesToken
&& FormulaGrammar::isExcelSyntax( meGrammar
) &&
1495 mpLastToken
&& mpToken
&&
1496 isPotentialRangeType( mpLastToken
.get(), false, false) &&
1497 isPotentialRangeType( mpToken
.get(), false, true))
1499 // Let IntersectionLine() <- Factor() decide how to treat this,
1500 // once the actual arguments are determined in RPN.
1501 mpLastToken
= mpToken
= pSpacesToken
;
1502 maArrIterator
.StepBack(); // step back from next non-spaces token
1509 mpLastToken
= mpToken
= new FormulaByteToken( ocStop
);
1513 // Remember token for next round and any PushTokenArray() calls that may
1514 // occur in handlers.
1515 mpLastToken
= mpToken
;
1517 if ( mpToken
->IsExternalRef() )
1519 return HandleExternalReference(*mpToken
);
1523 switch (mpToken
->GetOpCode())
1532 // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
1533 // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
1534 needsRPNTokenCheck
= true;
1539 return HandleColRowName();
1541 return HandleDbData();
1543 return HandleTableRef();
1546 HandleIIOpCode(mpToken
.get(), nullptr, 0);
1556 // RPN creation by recursion
1557 void FormulaCompiler::Factor()
1559 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
1562 CurrentFactor
pFacToken( this );
1564 OpCode eOp
= mpToken
->GetOpCode();
1565 if (eOp
== ocPush
|| eOp
== ocColRowNameAuto
|| eOp
== ocMatRef
|| eOp
== ocDBArea
1566 || eOp
== ocTableRef
1567 || (!mbJumpCommandReorder
&& ((eOp
== ocName
) || (eOp
== ocColRowName
) || (eOp
== ocBad
)))
1574 // PUSH( is an error that may be caused by an unknown function.
1576 ( mpToken
->GetType() == svString
1577 || mpToken
->GetType() == svSingleRef
)
1578 ? FormulaError::NoName
: FormulaError::OperatorExpected
);
1579 if ( bAutoCorrect
&& !pStack
)
1580 { // assume multiplication
1581 aCorrectedFormula
+= mxSymbols
->getSymbol( ocMul
);
1585 if( eOp
!= ocClose
)
1586 SetError( FormulaError::PairExpected
);
1592 else if( eOp
== ocOpen
)
1596 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1597 { // range list (A1;A2) converted to (A1~A2)
1598 pFacToken
= mpToken
;
1600 CheckSetForceArrayParameter( mpToken
, 0);
1602 // Do not ignore error here, regardless of mbStopOnError, to not
1603 // change the formula expression in case of an unexpected state.
1604 if (pArr
->GetCodeError() == FormulaError::NONE
&& pc
>= 2)
1606 // Left and right operands must be reference or function
1607 // returning reference to form a range list.
1608 const FormulaToken
* p
= pCode
[-2];
1609 if (p
&& isPotentialRangeType( p
, true, false))
1612 if (p
&& isPotentialRangeType( p
, true, true))
1614 pFacToken
->NewOpCode( ocUnion
, FormulaToken::PrivateAccess());
1615 // XXX NOTE: the token's eType is still svSep here!
1616 PutCode( pFacToken
);
1622 SetError( FormulaError::PairExpected
);
1626 /* TODO: if no conversion to ocUnion is involved this could collect
1627 * such expression as a list or (matrix) vector to be passed as
1628 * argument for one parameter (which in fact the ocUnion svRefList is a
1629 * special case of), which would require a new StackVar type and needed
1630 * to be handled by the interpreter for functions that could support it
1631 * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
1632 * not defined by ODF.
1633 * Does Excel handle =SUM((1;2))?
1634 * As is, the interpreter catches extraneous uncalculated
1635 * subexpressions like 1 of (1;2) as error. */
1639 if( nNumFmt
== SvNumFormatType::UNDEFINED
)
1640 nNumFmt
= lcl_GetRetFormat( eOp
);
1642 if ( IsOpCodeVolatile( eOp
) )
1643 pArr
->SetExclusiveRecalcModeAlways();
1648 // Functions recalculated on every document load.
1649 // ONLOAD_LENIENT here to be able to distinguish and not
1650 // force a recalc (if not in an ALWAYS or ONLOAD_MUST
1651 // context) but keep an imported result from for example
1652 // OOXML a DDE call. Will be recalculated for ODFF.
1657 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT
);
1659 // RANDBETWEEN() is volatile like RAND(). Other Add-In
1660 // functions may have to be recalculated or not, we don't
1661 // know, classify as ONLOAD_LENIENT.
1663 if (mpToken
->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
1664 pArr
->SetExclusiveRecalcModeAlways();
1666 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT
);
1668 // If the referred cell is moved the value changes.
1671 pArr
->SetRecalcModeOnRefMove();
1673 // ocCell needs recalc on move for some possible type values.
1674 // And recalc mode on load, tdf#60645
1676 pArr
->SetRecalcModeOnRefMove();
1677 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_MUST
);
1680 // Cell with hyperlink needs to be calculated on load to
1681 // get its matrix result generated.
1682 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_MUST
);
1683 pArr
->SetHyperLink( true);
1689 if (SC_OPCODE_START_NO_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_NO_PAR
)
1691 pFacToken
= mpToken
;
1695 SetError( FormulaError::PairExpected
);
1696 PutCode( pFacToken
);
1702 SetError( FormulaError::PairExpected
);
1703 PutCode( pFacToken
);
1707 else if (SC_OPCODE_START_1_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_1_PAR
)
1709 if (eOp
== ocIsoWeeknum
&& FormulaGrammar::isODFF( meGrammar
))
1711 // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
1712 // the opcode then has to be changed to ocWeek for backward compatibility
1713 pFacToken
= mpToken
;
1715 bool bNoParam
= false;
1723 CheckSetForceArrayParameter( mpToken
, 0);
1728 SetError( FormulaError::PairExpected
);
1729 sal_uInt32 nSepCount
= 0;
1730 const sal_uInt16 nSepPos
= maArrIterator
.GetIndex() - 1; // separator position, if any
1734 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1737 CheckSetForceArrayParameter( mpToken
, nSepCount
);
1739 if (nSepCount
> FORMULA_MAXPARAMS
)
1740 SetError( FormulaError::CodeOverflow
);
1745 SetError( FormulaError::PairExpected
);
1748 pFacToken
->SetByte( nSepCount
);
1751 // An old mode!=1 indicates ISO week, remove argument if
1752 // literal double value and keep function. Anything else
1753 // can not be resolved, there exists no "like ISO but week
1754 // starts on Sunday" mode in WEEKNUM and for an expression
1755 // we can't determine.
1756 // Current index is nSepPos+3 if expression stops, or
1757 // nSepPos+4 if expression continues after the call because
1758 // we just called NextToken() to move away from it.
1759 if (pc
>= 2 && (maArrIterator
.GetIndex() == nSepPos
+ 3 || maArrIterator
.GetIndex() == nSepPos
+ 4) &&
1760 pArr
->TokenAt(nSepPos
+1)->GetType() == svDouble
&&
1761 pArr
->TokenAt(nSepPos
+1)->GetDouble() != 1.0 &&
1762 pArr
->TokenAt(nSepPos
+2)->GetOpCode() == ocClose
&&
1763 pArr
->RemoveToken( nSepPos
, 2) == 2)
1765 maArrIterator
.AfterRemoveToken( nSepPos
, 2);
1766 // Remove the ocPush/svDouble just removed also from
1767 // the compiler local RPN array.
1769 (*pCode
)->DecRef(); // may be dead now
1770 pFacToken
->SetByte( nSepCount
- 1 );
1774 // For the remaining two arguments cases use the
1775 // compatibility function.
1776 pFacToken
->NewOpCode( ocWeeknumOOo
, FormulaToken::PrivateAccess());
1779 PutCode( pFacToken
);
1783 // standard handling of 1-parameter opcodes
1784 pFacToken
= mpToken
;
1786 if( nNumFmt
== SvNumFormatType::UNDEFINED
&& eOp
== ocNot
)
1787 nNumFmt
= SvNumFormatType::LOGICAL
;
1791 CheckSetForceArrayParameter( mpToken
, 0);
1795 SetError( FormulaError::PairExpected
);
1797 SetError( FormulaError::PairExpected
);
1798 else if ( pArr
->GetCodeError() == FormulaError::NONE
)
1800 pFacToken
->SetByte( 1 );
1803 FormulaToken
** pArg
= pCode
- 1;
1804 HandleIIOpCode(pFacToken
, &pArg
, 1);
1807 PutCode( pFacToken
);
1811 else if ((SC_OPCODE_START_2_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_2_PAR
)
1812 || eOp
== ocExternal
1817 || ( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
1818 || (!mbJumpCommandReorder
&& IsOpCodeJumpCommand(eOp
)))
1820 pFacToken
= mpToken
;
1821 OpCode eMyLastOp
= eOp
;
1823 bool bNoParam
= false;
1824 bool bBadName
= false;
1832 CheckSetForceArrayParameter( mpToken
, 0);
1836 else if (eMyLastOp
== ocBad
)
1838 // Just a bad name, not an unknown function, no parameters, no
1839 // closing expected.
1844 SetError( FormulaError::PairExpected
);
1845 sal_uInt32 nSepCount
= 0;
1848 bool bDoIICompute
= mbComputeII
;
1849 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1850 FormulaToken
*** pArgArray
= nullptr;
1853 pArgArray
= static_cast<FormulaToken
***>(alloca(sizeof(FormulaToken
**)*FORMULA_MAXPARAMSII
));
1855 bDoIICompute
= false;
1861 pArgArray
[nSepCount
-1] = pCode
- 1; // Add first argument
1863 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1866 CheckSetForceArrayParameter( mpToken
, nSepCount
);
1868 if (nSepCount
> FORMULA_MAXPARAMS
)
1869 SetError( FormulaError::CodeOverflow
);
1871 if (bDoIICompute
&& nSepCount
<= FORMULA_MAXPARAMSII
)
1872 pArgArray
[nSepCount
- 1] = pCode
- 1; // Add rest of the arguments
1875 HandleIIOpCode(pFacToken
, pArgArray
,
1876 std::min(nSepCount
, static_cast<sal_uInt32
>(FORMULA_MAXPARAMSII
)));
1880 ; // nothing, keep current token for return
1881 else if (eOp
!= ocClose
)
1882 SetError( FormulaError::PairExpected
);
1888 // Jumps are just normal functions for the FunctionAutoPilot tree view
1889 if (!mbJumpCommandReorder
&& pFacToken
->GetType() == svJump
)
1890 pFacToken
= new FormulaFAPToken( pFacToken
->GetOpCode(), nSepCount
, pFacToken
);
1892 pFacToken
->SetByte( nSepCount
);
1893 PutCode( pFacToken
);
1898 else if (IsOpCodeJumpCommand(eOp
))
1900 // the PC counters are -1
1901 pFacToken
= mpToken
;
1905 pFacToken
->GetJump()[ 0 ] = 3; // if, else, behind
1908 pFacToken
->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT
+ 1;
1912 pFacToken
->GetJump()[ 0 ] = 2; // if, behind
1915 SAL_WARN("formula.core","Jump OpCode: " << +eOp
);
1916 assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case");
1922 CheckSetForceArrayParameter( mpToken
, 0);
1926 SetError( FormulaError::PairExpected
);
1927 PutCode( pFacToken
);
1928 // During AutoCorrect (since pArr->GetCodeError() is
1929 // ignored) an unlimited ocIf would crash because
1930 // ScRawToken::Clone() allocates the JumpBuffer according to
1931 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1933 OpCode eFacOpCode
= pFacToken
->GetOpCode();
1940 nJumpMax
= FORMULA_MAXJUMPCOUNT
;
1947 // May happen only if PutCode(pFacToken) ran into overflow.
1949 assert(pc
== FORMULA_MAXTOKENS
&& pArr
->GetCodeError() != FormulaError::NONE
);
1953 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode
);
1954 assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
1956 short nJumpCount
= 0;
1957 while ( (nJumpCount
< (FORMULA_MAXJUMPCOUNT
- 1)) && (eOp
== ocSep
)
1958 && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1960 if ( ++nJumpCount
<= nJumpMax
)
1961 pFacToken
->GetJump()[nJumpCount
] = pc
-1;
1963 CheckSetForceArrayParameter( mpToken
, nJumpCount
- 1);
1965 // ocSep or ocClose terminate the subexpression
1969 SetError( FormulaError::PairExpected
);
1973 // always limit to nJumpMax, no arbitrary overwrites
1974 if ( ++nJumpCount
<= nJumpMax
)
1975 pFacToken
->GetJump()[ nJumpCount
] = pc
-1;
1976 eFacOpCode
= pFacToken
->GetOpCode();
1981 bLimitOk
= (nJumpCount
<= 3);
1984 bLimitOk
= (nJumpCount
< FORMULA_MAXJUMPCOUNT
);
1988 bLimitOk
= (nJumpCount
<= 2);
1991 // May happen only if PutCode(pFacToken) ran into overflow.
1992 // This may had resulted from a stacked token array and
1993 // error wasn't propagated so assert only the program
1996 assert(pc
== FORMULA_MAXTOKENS
);
2000 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode
);
2001 assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case");
2004 pFacToken
->GetJump()[ 0 ] = nJumpCount
;
2006 SetError( FormulaError::IllegalParameter
);
2009 else if ( eOp
== ocMissing
)
2014 else if ( eOp
== ocClose
)
2016 SetError( FormulaError::ParameterExpected
);
2018 else if ( eOp
== ocSep
)
2019 { // Subsequent ocSep
2020 SetError( FormulaError::ParameterExpected
);
2021 if ( bAutoCorrect
&& !pStack
)
2023 aCorrectedSymbol
.clear();
2027 else if ( mpToken
->IsExternalRef() )
2034 SetError( FormulaError::UnknownToken
);
2035 if ( bAutoCorrect
&& !pStack
)
2037 if ( eOp
== ocStop
)
2038 { // trailing operator w/o operand
2039 sal_Int32 nLen
= aCorrectedFormula
.getLength();
2041 aCorrectedFormula
= aCorrectedFormula
.copy( 0, nLen
- 1 );
2042 aCorrectedSymbol
.clear();
2050 void FormulaCompiler::RangeLine()
2053 while (mpToken
->GetOpCode() == ocRange
)
2055 FormulaToken
** pCode1
= pCode
- 1;
2056 FormulaTokenRef p
= mpToken
;
2059 FormulaToken
** pCode2
= pCode
- 1;
2060 if (!MergeRangeReference( pCode1
, pCode2
))
2065 void FormulaCompiler::IntersectionLine()
2068 while (mpToken
->GetOpCode() == ocIntersect
|| mpToken
->GetOpCode() == ocSpaces
)
2070 sal_uInt16 nCodeIndex
= maArrIterator
.GetIndex() - 1;
2071 FormulaToken
** pCode1
= pCode
- 1;
2072 FormulaTokenRef p
= mpToken
;
2075 FormulaToken
** pCode2
= pCode
- 1;
2076 if (p
->GetOpCode() == ocSpaces
)
2078 // Convert to intersection if both left and right are references or
2079 // functions (potentially returning references, if not then a space
2080 // or no space would be a syntax error anyway), not other operators
2081 // or operands. Else discard.
2082 if (isAdjacentOrGapRpnEnd( pc
, pCode
, pCode1
, pCode2
) && isIntersectable( pCode1
, pCode2
))
2084 FormulaTokenRef
pIntersect( new FormulaByteToken( ocIntersect
));
2085 // Replace ocSpaces with ocIntersect so that when switching
2086 // formula syntax the correct operator string is created.
2087 // coverity[freed_arg : FALSE] - FormulaTokenRef has a ref so ReplaceToken won't delete pIntersect
2088 pArr
->ReplaceToken( nCodeIndex
, pIntersect
.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY
);
2089 PutCode( pIntersect
);
2099 void FormulaCompiler::UnionLine()
2102 while (mpToken
->GetOpCode() == ocUnion
)
2104 FormulaTokenRef p
= mpToken
;
2111 void FormulaCompiler::UnaryLine()
2113 if( mpToken
->GetOpCode() == ocAdd
)
2115 else if (SC_OPCODE_START_UN_OP
<= mpToken
->GetOpCode() &&
2116 mpToken
->GetOpCode() < SC_OPCODE_STOP_UN_OP
)
2118 FormulaTokenRef p
= mpToken
;
2123 FormulaToken
** pArg
= pCode
- 1;
2124 HandleIIOpCode(p
.get(), &pArg
, 1);
2132 void FormulaCompiler::PostOpLine()
2135 while ( mpToken
->GetOpCode() == ocPercentSign
)
2136 { // this operator _follows_ its operand
2139 FormulaToken
** pArg
= pCode
- 1;
2140 HandleIIOpCode(mpToken
.get(), &pArg
, 1);
2147 void FormulaCompiler::PowLine()
2150 while (mpToken
->GetOpCode() == ocPow
)
2152 FormulaTokenRef p
= mpToken
;
2153 FormulaToken
** pArgArray
[2];
2155 pArgArray
[0] = pCode
- 1; // Add first argument
2160 pArgArray
[1] = pCode
- 1; // Add second argument
2161 HandleIIOpCode(p
.get(), pArgArray
, 2);
2167 void FormulaCompiler::MulDivLine()
2170 while (mpToken
->GetOpCode() == ocMul
|| mpToken
->GetOpCode() == ocDiv
)
2172 FormulaTokenRef p
= mpToken
;
2173 FormulaToken
** pArgArray
[2];
2175 pArgArray
[0] = pCode
- 1; // Add first argument
2180 pArgArray
[1] = pCode
- 1; // Add second argument
2181 HandleIIOpCode(p
.get(), pArgArray
, 2);
2187 void FormulaCompiler::AddSubLine()
2190 while (mpToken
->GetOpCode() == ocAdd
|| mpToken
->GetOpCode() == ocSub
)
2192 FormulaTokenRef p
= mpToken
;
2193 FormulaToken
** pArgArray
[2];
2195 pArgArray
[0] = pCode
- 1; // Add first argument
2200 pArgArray
[1] = pCode
- 1; // Add second argument
2201 HandleIIOpCode(p
.get(), pArgArray
, 2);
2207 void FormulaCompiler::ConcatLine()
2210 while (mpToken
->GetOpCode() == ocAmpersand
)
2212 FormulaTokenRef p
= mpToken
;
2213 FormulaToken
** pArgArray
[2];
2215 pArgArray
[0] = pCode
- 1; // Add first argument
2220 pArgArray
[1] = pCode
- 1; // Add second argument
2221 HandleIIOpCode(p
.get(), pArgArray
, 2);
2227 void FormulaCompiler::CompareLine()
2230 while (mpToken
->GetOpCode() >= ocEqual
&& mpToken
->GetOpCode() <= ocGreaterEqual
)
2232 FormulaTokenRef p
= mpToken
;
2233 FormulaToken
** pArgArray
[2];
2235 pArgArray
[0] = pCode
- 1; // Add first argument
2240 pArgArray
[1] = pCode
- 1; // Add second argument
2241 HandleIIOpCode(p
.get(), pArgArray
, 2);
2247 OpCode
FormulaCompiler::Expression()
2249 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
2250 if ( nRecursion
> nRecursionMax
)
2252 SetError( FormulaError::StackOverflow
);
2253 return ocStop
; //! generate token instead?
2256 while (mpToken
->GetOpCode() == ocAnd
|| mpToken
->GetOpCode() == ocOr
)
2258 FormulaTokenRef p
= mpToken
;
2259 mpToken
->SetByte( 2 ); // 2 parameters!
2260 FormulaToken
** pArgArray
[2];
2262 pArgArray
[0] = pCode
- 1; // Add first argument
2267 pArgArray
[1] = pCode
- 1; // Add second argument
2268 HandleIIOpCode(p
.get(), pArgArray
, 2);
2272 return mpToken
->GetOpCode();
2276 void FormulaCompiler::SetError( FormulaError
/*nError*/ )
2280 FormulaTokenRef
FormulaCompiler::ExtendRangeReference( FormulaToken
& /*rTok1*/, FormulaToken
& /*rTok2*/ )
2282 return FormulaTokenRef();
2285 bool FormulaCompiler::MergeRangeReference( FormulaToken
* * const pCode1
, FormulaToken
* const * const pCode2
)
2287 if (!isAdjacentRpnEnd( pc
, pCode
, pCode1
, pCode2
))
2290 FormulaToken
*p1
= *pCode1
, *p2
= *pCode2
;
2291 FormulaTokenRef p
= ExtendRangeReference( *p1
, *p2
);
2305 bool FormulaCompiler::CompileTokenArray()
2309 needsRPNTokenCheck
= false;
2310 if (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
)
2314 aCorrectedFormula
.clear();
2315 aCorrectedSymbol
.clear();
2318 maArrIterator
.Reset();
2320 FormulaToken
* pDataArray
[ FORMULA_MAXTOKENS
+ 1 ];
2321 // Code in some places refers to the last token as 'pCode - 1', which may
2322 // point before the first element if the expression is bad. So insert a dummy
2323 // node in that place which will make that token be nullptr.
2324 pDataArray
[ 0 ] = nullptr;
2325 FormulaToken
** pData
= pDataArray
+ 1;
2327 bool bWasForced
= pArr
->IsRecalcModeForced();
2328 if ( bWasForced
&& bAutoCorrect
)
2329 aCorrectedFormula
= "=";
2330 pArr
->ClearRecalcMode();
2331 maArrIterator
.Reset();
2335 OpCode eOp
= Expression();
2336 // Some trailing garbage that doesn't form an expression?
2338 SetError( FormulaError::OperatorExpected
);
2341 FormulaError nErrorBeforePop
= pArr
->GetCodeError();
2347 pArr
->CreateNewRPNArrayFromData( pData
, pc
);
2348 if( needsRPNTokenCheck
)
2349 pArr
->CheckAllRPNTokens();
2352 // once an error, always an error
2353 if( pArr
->GetCodeError() == FormulaError::NONE
&& nErrorBeforePop
!= FormulaError::NONE
)
2354 pArr
->SetCodeError( nErrorBeforePop
);
2356 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
2359 maArrIterator
.Reset();
2360 pArr
->SetHyperLink( false);
2364 pArr
->SetRecalcModeForced();
2366 if( nNumFmt
== SvNumFormatType::UNDEFINED
)
2367 nNumFmt
= SvNumFormatType::NUMBER
;
2371 void FormulaCompiler::PopTokenArray()
2376 FormulaArrayStack
* p
= pStack
;
2378 // obtain special RecalcMode from SharedFormula
2379 if ( pArr
->IsRecalcModeAlways() )
2380 p
->pArr
->SetExclusiveRecalcModeAlways();
2381 else if ( !pArr
->IsRecalcModeNormal() && p
->pArr
->IsRecalcModeNormal() )
2382 p
->pArr
->SetMaskedRecalcMode( pArr
->GetRecalcMode() );
2383 p
->pArr
->SetCombinedBitsRecalcMode( pArr
->GetRecalcMode() );
2384 if ( pArr
->IsHyperLink() ) // fdo 87534
2385 p
->pArr
->SetHyperLink( true );
2389 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
2390 maArrIterator
.Jump(p
->nIndex
);
2391 mpLastToken
= p
->mpLastToken
;
2395 void FormulaCompiler::CreateStringFromTokenArray( OUString
& rFormula
)
2397 OUStringBuffer
aBuffer( pArr
->GetLen() * 5 );
2398 CreateStringFromTokenArray( aBuffer
);
2399 rFormula
= aBuffer
.makeStringAndClear();
2402 void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer
& rBuffer
)
2404 rBuffer
.setLength(0);
2405 if( !pArr
->GetLen() )
2408 FormulaTokenArray
* pSaveArr
= pArr
;
2409 int nSaveIndex
= maArrIterator
.GetIndex();
2410 bool bODFF
= FormulaGrammar::isODFF( meGrammar
);
2411 if (bODFF
|| FormulaGrammar::isPODF( meGrammar
) )
2413 // Scan token array for missing args and re-write if present.
2414 MissingConventionODF
aConv( bODFF
);
2415 if (pArr
->NeedsPodfRewrite( aConv
))
2417 pArr
= pArr
->RewriteMissing( aConv
);
2418 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2421 else if ( FormulaGrammar::isOOXML( meGrammar
) )
2423 // Scan token array for missing args and rewrite if present.
2424 if (pArr
->NeedsOoxmlRewrite())
2426 MissingConventionOOXML aConv
;
2427 pArr
= pArr
->RewriteMissing( aConv
);
2428 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2432 // At least one character per token, plus some are references, some are
2433 // function names, some are numbers, ...
2434 rBuffer
.ensureCapacity( pArr
->GetLen() * 5 );
2436 if ( pArr
->IsRecalcModeForced() )
2437 rBuffer
.append( '=');
2438 const FormulaToken
* t
= maArrIterator
.First();
2440 t
= CreateStringFromToken( rBuffer
, t
, true );
2442 if (pSaveArr
!= pArr
)
2446 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2447 maArrIterator
.Jump(nSaveIndex
);
2451 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUString
& rFormula
, const FormulaToken
* pTokenP
)
2453 OUStringBuffer aBuffer
;
2454 const FormulaToken
* p
= CreateStringFromToken( aBuffer
, pTokenP
);
2455 rFormula
+= aBuffer
;
2459 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUStringBuffer
& rBuffer
, const FormulaToken
* pTokenP
,
2460 bool bAllowArrAdvance
)
2463 bool bSpaces
= false;
2464 const FormulaToken
* t
= pTokenP
;
2465 OpCode eOp
= t
->GetOpCode();
2466 if( eOp
>= ocAnd
&& eOp
<= ocOr
)
2469 if ( bAllowArrAdvance
)
2470 t
= maArrIterator
.Next();
2472 t
= maArrIterator
.PeekNext();
2474 bSpaces
= ( !t
|| t
->GetOpCode() != ocOpen
);
2477 rBuffer
.append( ' ');
2479 if (eOp
== ocSpaces
|| eOp
== ocWhitespace
)
2481 bool bWriteSpaces
= true;
2482 if (eOp
== ocSpaces
&& mxSymbols
->isODFF())
2484 const FormulaToken
* p
= maArrIterator
.PeekPrevNoSpaces();
2485 bool bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
2486 if (bIntersectionOp
)
2488 p
= maArrIterator
.PeekNextNoSpaces();
2489 bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
2491 if (bIntersectionOp
)
2493 rBuffer
.append( "!!");
2494 bWriteSpaces
= false;
2499 // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
2500 // not separate a function name from its initial opening
2503 // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
2504 // shall separate a function-name from the left parenthesis (()
2505 // that follows it." and Excel even chokes on it.
2507 // Suppress/remove it in any case also in UI, it will not be
2509 const FormulaToken
* p
= maArrIterator
.PeekPrevNoSpaces();
2510 if (p
&& p
->IsFunction())
2512 p
= maArrIterator
.PeekNextNoSpaces();
2513 if (p
&& p
->GetOpCode() == ocOpen
)
2514 bWriteSpaces
= false;
2519 // most times it's just one blank
2520 sal_uInt8 n
= t
->GetByte();
2521 for ( sal_uInt8 j
=0; j
<n
; ++j
)
2523 if (eOp
== ocWhitespace
)
2524 rBuffer
.append( t
->GetChar());
2526 rBuffer
.append( ' ');
2530 else if( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
2531 rBuffer
.appendAscii( pInternal
[ eOp
- ocInternalBegin
] );
2532 else if (eOp
== ocIntersect
)
2534 // Nasty, ugly, horrific, terrifying...
2535 if (FormulaGrammar::isExcelSyntax( meGrammar
))
2536 rBuffer
.append(' ');
2538 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
2540 else if( static_cast<sal_uInt16
>(eOp
) < mxSymbols
->getSymbolCount()) // Keyword:
2541 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
2544 SAL_WARN( "formula.core","unknown OpCode");
2545 rBuffer
.append( GetNativeSymbol( ocErrName
));
2549 if (t
->IsExternalRef())
2551 CreateStringFromExternal( rBuffer
, pTokenP
);
2555 switch( t
->GetType() )
2558 AppendDouble( rBuffer
, t
->GetDouble() );
2562 if( eOp
== ocBad
|| eOp
== ocStringXML
)
2563 rBuffer
.append( t
->GetString().getString());
2565 AppendString( rBuffer
, t
->GetString().getString() );
2568 CreateStringFromSingleRef( rBuffer
, t
);
2571 CreateStringFromDoubleRef( rBuffer
, t
);
2575 CreateStringFromMatrix( rBuffer
, t
);
2579 CreateStringFromIndex( rBuffer
, t
);
2580 if (t
->GetOpCode() == ocTableRef
&& bAllowArrAdvance
&& NeedsTableRefTransformation())
2582 // Suppress all TableRef related tokens, the resulting
2583 // range was written by CreateStringFromIndex().
2584 const FormulaToken
* const p
= maArrIterator
.PeekNext();
2585 if (p
&& p
->GetOpCode() == ocTableRefOpen
)
2590 t
= maArrIterator
.Next();
2594 // Switch cases correspond with those in
2595 // ScCompiler::HandleTableRef()
2596 switch (t
->GetOpCode())
2598 case ocTableRefOpen
:
2601 case ocTableRefClose
:
2604 case ocTableRefItemAll
:
2605 case ocTableRefItemHeaders
:
2606 case ocTableRefItemData
:
2607 case ocTableRefItemTotals
:
2608 case ocTableRefItemThisRow
:
2625 // mapped or translated name of AddIns
2626 OUString
aAddIn( t
->GetExternal() );
2627 bool bMapped
= mxSymbols
->isPODF(); // ODF 1.1 directly uses programmatical name
2628 if (!bMapped
&& mxSymbols
->hasExternals())
2630 ExternalHashMap::const_iterator iLook
= mxSymbols
->getReverseExternalHashMap().find( aAddIn
);
2631 if (iLook
!= mxSymbols
->getReverseExternalHashMap().end())
2633 aAddIn
= (*iLook
).second
;
2637 if (!bMapped
&& !mxSymbols
->isEnglish())
2638 LocalizeString( aAddIn
);
2639 rBuffer
.append( aAddIn
);
2643 AppendErrorConstant( rBuffer
, t
->GetError());
2652 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t
->GetType());
2657 rBuffer
.append( ' ');
2658 if ( bAllowArrAdvance
)
2661 t
= maArrIterator
.Next();
2668 void FormulaCompiler::AppendDouble( OUStringBuffer
& rBuffer
, double fVal
) const
2670 if ( mxSymbols
->isEnglishLocale() )
2672 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
2673 rtl_math_StringFormat_Automatic
,
2674 rtl_math_DecimalPlaces_Max
, '.', true );
2678 SvtSysLocale aSysLocale
;
2679 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
2680 rtl_math_StringFormat_Automatic
,
2681 rtl_math_DecimalPlaces_Max
,
2682 aSysLocale
.GetLocaleData().getNumDecimalSep()[0],
2687 void FormulaCompiler::AppendBoolean( OUStringBuffer
& rBuffer
, bool bVal
) const
2689 rBuffer
.append( mxSymbols
->getSymbol( bVal
? ocTrue
: ocFalse
) );
2692 void FormulaCompiler::AppendString( OUStringBuffer
& rBuffer
, const OUString
& rStr
)
2694 rBuffer
.append( '"');
2695 if ( lcl_UnicodeStrChr( rStr
.getStr(), '"' ) == nullptr )
2696 rBuffer
.append( rStr
);
2699 OUString aStr
= rStr
.replaceAll( "\"", "\"\"" );
2700 rBuffer
.append(aStr
);
2702 rBuffer
.append( '"');
2705 bool FormulaCompiler::NeedsTableRefTransformation() const
2707 // Currently only UI representations and OOXML export use Table structured
2708 // references. Not defined in ODFF.
2709 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2710 // symbol is not defined there.
2711 return mxSymbols
->getSymbol( ocTableRefOpen
).isEmpty() || FormulaGrammar::isPODF( meGrammar
);
2714 void FormulaCompiler::UpdateSeparatorsNative(
2715 const OUString
& rSep
, const OUString
& rArrayColSep
, const OUString
& rArrayRowSep
)
2717 NonConstOpCodeMapPtr xSymbolsNative
;
2718 lcl_fillNativeSymbols( xSymbolsNative
);
2719 xSymbolsNative
->putOpCode( rSep
, ocSep
, nullptr);
2720 xSymbolsNative
->putOpCode( rArrayColSep
, ocArrayColSep
, nullptr);
2721 xSymbolsNative
->putOpCode( rArrayRowSep
, ocArrayRowSep
, nullptr);
2724 void FormulaCompiler::ResetNativeSymbols()
2726 NonConstOpCodeMapPtr xSymbolsNative
;
2727 lcl_fillNativeSymbols( xSymbolsNative
, InitSymbols::DESTROY
);
2728 lcl_fillNativeSymbols( xSymbolsNative
);
2731 void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr
& xMap
)
2733 NonConstOpCodeMapPtr xSymbolsNative
;
2734 lcl_fillNativeSymbols( xSymbolsNative
);
2735 xSymbolsNative
->copyFrom( *xMap
);
2739 OpCode
FormulaCompiler::NextToken()
2743 OpCode eOp
= mpToken
->GetOpCode();
2744 // There must be an operator before a push
2745 if ( (eOp
== ocPush
|| eOp
== ocColRowNameAuto
) &&
2746 !( (eLastOp
== ocOpen
) || (eLastOp
== ocSep
) ||
2747 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)) )
2748 SetError( FormulaError::OperatorExpected
);
2749 // Operator and Plus => operator
2750 if (eOp
== ocAdd
&& (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2751 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2753 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
2758 // Before an operator there must not be another operator, with the
2759 // exception of AND and OR.
2760 if ( eOp
!= ocAnd
&& eOp
!= ocOr
&&
2761 (SC_OPCODE_START_BIN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_BIN_OP
)
2762 && (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2763 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2765 SetError( FormulaError::VariableExpected
);
2766 if ( bAutoCorrect
&& !pStack
)
2768 if ( eOp
== eLastOp
|| eLastOp
== ocOpen
)
2769 { // throw away duplicated operator
2770 aCorrectedSymbol
.clear();
2775 sal_Int32 nPos
= aCorrectedFormula
.getLength();
2779 sal_Unicode c
= aCorrectedFormula
[ nPos
];
2783 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2784 { // >= instead of =>
2785 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2786 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocGreater
) ) );
2787 aCorrectedSymbol
= OUString(c
);
2792 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2793 { // <= instead of =<
2794 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2795 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocLess
) ) );
2796 aCorrectedSymbol
= OUString(c
);
2799 else if ( c
== mxSymbols
->getSymbolChar( ocGreater
) )
2800 { // <> instead of ><
2801 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2802 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocLess
) ) );
2803 aCorrectedSymbol
= OUString(c
);
2808 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2809 { // *- instead of -*
2810 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2811 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocMul
) ) );
2812 aCorrectedSymbol
= OUString(c
);
2817 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2818 { // /- instead of -/
2819 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2820 rtl::OUStringChar( mxSymbols
->getSymbolChar(ocDiv
) ) );
2821 aCorrectedSymbol
= OUString(c
);
2832 // Nasty, ugly, horrific, terrifying... significant whitespace...
2833 if (eOp
== ocSpaces
&& FormulaGrammar::isExcelSyntax( meGrammar
))
2835 // Fake an intersection op as last op for the next round, but at
2836 // least roughly check if it could make sense at all.
2837 FormulaToken
* pPrev
= maArrIterator
.PeekPrevNoSpaces();
2838 if (pPrev
&& isPotentialRangeType( pPrev
, false, false))
2840 FormulaToken
* pNext
= maArrIterator
.PeekNextNoSpaces();
2841 if (pNext
&& isPotentialRangeType( pNext
, false, true))
2842 eLastOp
= ocIntersect
;
2855 void FormulaCompiler::PutCode( FormulaTokenRef
& p
)
2857 if( pc
>= FORMULA_MAXTOKENS
- 1 )
2859 if ( pc
== FORMULA_MAXTOKENS
- 1 )
2861 SAL_WARN("formula.core", "FormulaCompiler::PutCode - CodeOverflow with OpCode " << +p
->GetOpCode());
2862 p
= new FormulaByteToken( ocStop
);
2867 SetError( FormulaError::CodeOverflow
);
2870 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbJumpCommandReorder
)
2872 ForceArrayOperator( p
);
2879 bool FormulaCompiler::HandleExternalReference( const FormulaToken
& /*_aToken*/)
2884 bool FormulaCompiler::HandleRange()
2889 bool FormulaCompiler::HandleColRowName()
2894 bool FormulaCompiler::HandleDbData()
2899 bool FormulaCompiler::HandleTableRef()
2904 void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2908 void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2912 void FormulaCompiler::CreateStringFromIndex( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2916 void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2920 void FormulaCompiler::CreateStringFromExternal( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2924 void FormulaCompiler::LocalizeString( OUString
& /*rName*/ ) const
2928 formula::ParamClass
FormulaCompiler::GetForceArrayParameter( const FormulaToken
* /*pToken*/, sal_uInt16
/*nParam*/ ) const
2930 return ParamClass::Unknown
;
2933 void FormulaCompiler::ForceArrayOperator( FormulaTokenRef
const & rCurr
)
2935 if (pCurrentFactorToken
.get() == rCurr
.get())
2938 const OpCode eOp
= rCurr
->GetOpCode();
2939 const StackVar eType
= rCurr
->GetType();
2940 const bool bInlineArray
= (eOp
== ocPush
&& eType
== svMatrix
);
2944 if (rCurr
->GetInForceArray() != ParamClass::Unknown
)
2945 // Already set, unnecessary to evaluate again. This happens by calls to
2946 // CurrentFactor::operator=() while descending through Factor() and
2947 // then ascending back (and down and up, ...),
2948 // CheckSetForceArrayParameter() and later PutCode().
2951 if (!(eOp
!= ocPush
&& (eType
== svByte
|| eType
== svJump
)))
2955 // Return class for inline arrays and functions returning array/matrix.
2956 // It's somewhat unclear what Excel actually does there and in
2957 // ECMA-376-1:2016 OOXML mentions "call to ... shall be an array formula"
2958 // only for FREQUENCY() and TRANSPOSE() but not for any other function
2959 // returning array/matrix or inline arrays, though for the latter has one
2960 // example in 18.17.2 Syntax:
2961 // "SUM(SQRT({1,2,3,4})) returns 6.14 when entered normally". However,
2962 // these need to be treated similar but not as ParamClass::ForceArray
2963 // (which would contradict the example in
2964 // https://bugs.documentfoundation.org/show_bug.cgi?id=122301#c19 and A6 of
2965 // https://bugs.documentfoundation.org/show_bug.cgi?id=133260#c10 ).
2967 // commit d0ded163d8e93dc5b10d7a7c9bdab1d0a6a50bac
2968 // commit 5413c8871dec08eff19f514f5f391b946a45c86c
2969 constexpr ParamClass eArrayReturn
= ParamClass::ForceArrayReturn
;
2973 // rCurr->SetInForceArray() can not be used with ocPush, but ocPush
2974 // with svMatrix has an implicit ParamClass::ForceArrayReturn.
2975 if (nCurrentFactorParam
> 0 && pCurrentFactorToken
2976 && pCurrentFactorToken
->GetInForceArray() == ParamClass::Unknown
2977 && GetForceArrayParameter( pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1))
2978 == ParamClass::Value
)
2980 // Propagate to caller as if a function returning an array/matrix
2981 // was called (see also below).
2982 pCurrentFactorToken
->SetInForceArray( eArrayReturn
);
2987 if (!pCurrentFactorToken
)
2991 // An array/matrix formula acts as ForceArray on all top level
2992 // operators and function calls, so that can be inherited properly
2994 rCurr
->SetInForceArray( ParamClass::ForceArray
);
2996 else if (pc
>= 2 && SC_OPCODE_START_BIN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_BIN_OP
)
2998 // Binary operators are not functions followed by arguments
2999 // and need some peeking into RPN to inspect their operands.
3000 // Note that array context is not forced if only one
3001 // of the operands is an array like "={1;2}+A1:A2" returns #VALUE!
3002 // if entered in column A and not input in array mode, because it
3003 // involves a range reference with an implicit intersection. Check
3004 // both arguments are arrays, or the other is ocPush without ranges
3005 // for "={1;2}+3" or "={1;2}+A1".
3006 // Note this does not catch "={1;2}+ABS(A1)" that could be forced
3007 // to array, user still has to close in array mode.
3008 // The IsMatrixFunction() is only necessary because not all
3009 // functions returning matrix have ForceArrayReturn (yet?), see
3010 // OOXML comment above.
3012 const OpCode eOp1
= pCode
[-1]->GetOpCode();
3013 const OpCode eOp2
= pCode
[-2]->GetOpCode();
3014 const bool b1
= (pCode
[-1]->GetInForceArray() != ParamClass::Unknown
|| IsMatrixFunction(eOp1
));
3015 const bool b2
= (pCode
[-2]->GetInForceArray() != ParamClass::Unknown
|| IsMatrixFunction(eOp2
));
3017 || (b1
&& eOp2
== ocPush
&& pCode
[-2]->GetType() != svDoubleRef
)
3018 || (b2
&& eOp1
== ocPush
&& pCode
[-1]->GetType() != svDoubleRef
))
3020 rCurr
->SetInForceArray( eArrayReturn
);
3023 else if (pc
>= 1 && SC_OPCODE_START_UN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_UN_OP
)
3025 // Similar for unary operators.
3026 if (pCode
[-1]->GetInForceArray() != ParamClass::Unknown
|| IsMatrixFunction(pCode
[-1]->GetOpCode()))
3028 rCurr
->SetInForceArray( eArrayReturn
);
3034 // Inherited parameter class.
3035 const formula::ParamClass eForceType
= pCurrentFactorToken
->GetInForceArray();
3036 if (eForceType
== ParamClass::ForceArray
|| eForceType
== ParamClass::ReferenceOrRefArray
)
3038 // ReferenceOrRefArray was set only if in ForceArray context already,
3039 // it is valid for the one function only to indicate the preferred
3040 // return type. Propagate as ForceArray if not another parameter
3041 // handling ReferenceOrRefArray.
3042 if (nCurrentFactorParam
> 0
3043 && (GetForceArrayParameter( pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1))
3044 == ParamClass::ReferenceOrRefArray
))
3045 rCurr
->SetInForceArray( ParamClass::ReferenceOrRefArray
);
3047 rCurr
->SetInForceArray( ParamClass::ForceArray
);
3050 else if (eForceType
== ParamClass::ReferenceOrForceArray
)
3052 // Inherit further only if the return class of the nested function is
3053 // not Reference. Else flag as suppressed.
3054 if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) != ParamClass::Reference
)
3055 rCurr
->SetInForceArray( eForceType
);
3057 rCurr
->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray
);
3061 if (nCurrentFactorParam
<= 0)
3064 // Actual current parameter's class.
3065 const formula::ParamClass eParamType
= GetForceArrayParameter(
3066 pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1));
3067 if (eParamType
== ParamClass::ForceArray
)
3068 rCurr
->SetInForceArray( eParamType
);
3069 else if (eParamType
== ParamClass::ReferenceOrForceArray
)
3071 if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) != ParamClass::Reference
)
3072 rCurr
->SetInForceArray( eParamType
);
3074 rCurr
->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray
);
3077 // Propagate a ForceArrayReturn to caller if the called function
3078 // returns one and the caller so far does not have a stronger array
3079 // mode set and expects a scalar value for this parameter.
3080 if (eParamType
== ParamClass::Value
&& pCurrentFactorToken
->GetInForceArray() == ParamClass::Unknown
)
3082 if (IsMatrixFunction( eOp
))
3083 pCurrentFactorToken
->SetInForceArray( eArrayReturn
);
3084 else if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) == ParamClass::ForceArrayReturn
)
3085 pCurrentFactorToken
->SetInForceArray( ParamClass::ForceArrayReturn
);
3089 void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef
const & rCurr
, sal_uInt8 nParam
)
3091 if (!pCurrentFactorToken
)
3094 nCurrentFactorParam
= nParam
+ 1;
3096 ForceArrayOperator( rCurr
);
3099 void FormulaCompiler::PushTokenArray( FormulaTokenArray
* pa
, bool bTemp
)
3101 if ( bAutoCorrect
&& !pStack
)
3102 { // don't merge stacked subroutine code into entered formula
3103 aCorrectedFormula
+= aCorrectedSymbol
;
3104 aCorrectedSymbol
.clear();
3106 FormulaArrayStack
* p
= new FormulaArrayStack
;
3109 p
->nIndex
= maArrIterator
.GetIndex();
3110 p
->mpLastToken
= mpLastToken
;
3114 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
3117 } // namespace formula
3119 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */