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 <formula/FormulaCompiler.hxx>
22 #include <formula/errorcodes.hxx>
23 #include <formula/token.hxx>
24 #include <formula/tokenarray.hxx>
25 #include <core_resource.hxx>
26 #include <core_resource.hrc>
28 #include <osl/mutex.hxx>
30 #include <svl/zforlist.hxx>
31 #include <unotools/charclass.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vcl/settings.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
36 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
37 #include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
42 using namespace ::com::sun::star
;
44 static const char* pInternal
[2] = { "TTT", "__DEBUG_VAR" };
48 class FormulaCompilerRecursionGuard
53 explicit FormulaCompilerRecursionGuard( short& rRec
)
54 : rRecursion( rRec
) { ++rRecursion
; }
55 ~FormulaCompilerRecursionGuard() { --rRecursion
; }
58 SvNumFormatType
lcl_GetRetFormat( OpCode eOpCode
)
87 return SvNumFormatType::LOGICAL
;
91 return SvNumFormatType::DATE
;
93 return SvNumFormatType::DATETIME
;
95 return SvNumFormatType::TIME
;
109 return SvNumFormatType::CURRENCY
;
117 return SvNumFormatType::PERCENT
;
119 return SvNumFormatType::NUMBER
;
123 void lclPushOpCodeMapEntry( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
124 const OUString
* pTable
, sal_uInt16 nOpCode
)
126 sheet::FormulaOpCodeMapEntry aEntry
;
127 aEntry
.Token
.OpCode
= nOpCode
;
128 aEntry
.Name
= pTable
[nOpCode
];
129 rVec
.push_back( aEntry
);
132 void lclPushOpCodeMapEntries( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
133 const OUString
* pTable
, sal_uInt16 nOpCodeBeg
, sal_uInt16 nOpCodeEnd
)
135 for (sal_uInt16 nOpCode
= nOpCodeBeg
; nOpCode
< nOpCodeEnd
; ++nOpCode
)
136 lclPushOpCodeMapEntry( rVec
, pTable
, nOpCode
);
139 void lclPushOpCodeMapEntries( ::std::vector
< sheet::FormulaOpCodeMapEntry
>& rVec
,
140 const OUString
* pTable
, const sal_uInt16
* pnOpCodes
, size_t nCount
)
142 for (const sal_uInt16
* pnEnd
= pnOpCodes
+ nCount
; pnOpCodes
< pnEnd
; ++pnOpCodes
)
143 lclPushOpCodeMapEntry( rVec
, pTable
, *pnOpCodes
);
146 CharClass
* createCharClassIfNonEnglishUI()
148 const LanguageTag
& rLanguageTag( Application::GetSettings().GetUILanguageTag());
149 if (rLanguageTag
.getLanguage() == "en")
151 return new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag
);
158 OpCodeList(bool bLocalized
, const std::pair
<const char*, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
&,
159 FormulaCompiler::SeparatorType
= FormulaCompiler::SeparatorType::SEMICOLON_BASE
);
162 bool getOpCodeString( OUString
& rStr
, sal_uInt16 nOp
);
163 void putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, sal_uInt16 nOp
, const CharClass
* pCharClass
);
166 FormulaCompiler::SeparatorType meSepType
;
167 const std::pair
<const char*, int>* mpSymbols
;
171 OpCodeList::OpCodeList(bool bLocalized
, const std::pair
<const char*, int>* pSymbols
, const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
,
172 FormulaCompiler::SeparatorType eSepType
)
173 : meSepType(eSepType
)
174 , mpSymbols(pSymbols
)
175 , mbLocalized(bLocalized
)
177 std::unique_ptr
<CharClass
> xCharClass( xMap
->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
178 const CharClass
* pCharClass
= xCharClass
.get();
179 if (meSepType
== FormulaCompiler::SeparatorType::RESOURCE_BASE
)
181 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
183 putDefaultOpCode( xMap
, i
, pCharClass
);
188 for (sal_uInt16 i
= 0; i
<= SC_OPCODE_LAST_OPCODE_ID
; ++i
)
191 if ( getOpCodeString( aOpStr
, i
) )
192 xMap
->putOpCode( aOpStr
, OpCode(i
), pCharClass
);
194 putDefaultOpCode( xMap
, i
, pCharClass
);
199 bool OpCodeList::getOpCodeString( OUString
& rStr
, sal_uInt16 nOp
)
205 if (meSepType
== FormulaCompiler::SeparatorType::SEMICOLON_BASE
)
212 case SC_OPCODE_ARRAY_COL_SEP
:
214 if (meSepType
== FormulaCompiler::SeparatorType::SEMICOLON_BASE
)
221 case SC_OPCODE_ARRAY_ROW_SEP
:
223 if (meSepType
== FormulaCompiler::SeparatorType::SEMICOLON_BASE
)
235 void OpCodeList::putDefaultOpCode( const FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, sal_uInt16 nOp
,
236 const CharClass
* pCharClass
)
238 const char* pKey
= nullptr;
239 for (const std::pair
<const char*, int>* pSymbol
= mpSymbols
; pSymbol
->first
; ++pSymbol
)
241 if (nOp
== pSymbol
->second
)
243 pKey
= pSymbol
->first
;
249 OUString sKey
= !mbLocalized
? OUString::createFromAscii(pKey
) : ForResId(pKey
);
250 xMap
->putOpCode(sKey
, OpCode(nOp
), pCharClass
);
254 const sal_Unicode
* lcl_UnicodeStrChr( const sal_Unicode
* pStr
, sal_Unicode c
)
269 FormulaCompiler::NonConstOpCodeMapPtr mxSymbolMap
;
274 bool isPotentialRangeLeftOp( OpCode eOp
)
285 bool isRangeResultFunction( OpCode eOp
)
297 bool isRangeResultOpCode( OpCode eOp
)
314 MUST be a valid token, caller has to ensure.
317 If bRPN==false, bRight==false means opcodes for left side are
318 checked, bRight==true means opcodes for right side. If bRPN==true
319 it doesn't matter except for the ocSep converted to ocUnion case.
321 bool isPotentialRangeType( FormulaToken
const * pToken
, bool bRPN
, bool bRight
)
323 switch (pToken
->GetType())
325 case svByte
: // could be range result, but only a few
327 return isRangeResultOpCode( pToken
->GetOpCode());
329 return isRangeResultFunction( pToken
->GetOpCode());
331 return isPotentialRangeLeftOp( pToken
->GetOpCode());
334 case svIndex
: // could be range
335 //case svRefList: // um..what?
336 case svExternalSingleRef
:
337 case svExternalDoubleRef
:
338 case svExternalName
: // could be range
341 // A special case if a previous ocSep was converted to ocUnion it
342 // stays svSep instead of svByte.
343 return bRPN
&& !bRight
&& pToken
->GetOpCode() == ocUnion
;
345 // Separators are not part of RPN and right opcodes need to be
346 // other StackVar types or functions and thus svByte.
347 return !bRPN
&& !bRight
&& isPotentialRangeLeftOp( pToken
->GetOpCode());
351 bool isIntersectable( FormulaToken
** pCode1
, FormulaToken
** pCode2
)
353 FormulaToken
* pToken1
= *pCode1
;
354 FormulaToken
* pToken2
= *pCode2
;
355 if (pToken1
&& pToken2
)
356 return isPotentialRangeType( pToken1
, true, false) && isPotentialRangeType( pToken2
, true, true);
360 bool isAdjacentRpnEnd( sal_uInt16 nPC
,
361 FormulaToken
const * const * const pCode
,
362 FormulaToken
const * const * const pCode1
,
363 FormulaToken
const * const * const pCode2
)
365 return nPC
>= 2 && pCode1
&& pCode2
&&
366 (pCode2
- pCode1
== 1) && (pCode
- pCode2
== 1) &&
367 (*pCode1
!= nullptr) && (*pCode2
!= nullptr);
370 bool isAdjacentOrGapRpnEnd( sal_uInt16 nPC
,
371 FormulaToken
const * const * const pCode
,
372 FormulaToken
const * const * const pCode1
,
373 FormulaToken
const * const * const pCode2
)
375 return nPC
>= 2 && pCode1
&& pCode2
&&
376 (pCode2
> pCode1
) && (pCode
- pCode2
== 1) &&
377 (*pCode1
!= nullptr) && (*pCode2
!= nullptr);
384 void FormulaCompiler::OpCodeMap::putExternal( const OUString
& rSymbol
, const OUString
& rAddIn
)
386 // Different symbols may map to the same AddIn, but the same AddIn may not
387 // map to different symbols, the first pair wins. Same symbol of course may
388 // not map to different AddIns, again the first pair wins and also the
389 // AddIn->symbol mapping is not inserted in other cases.
390 bool bOk
= maExternalHashMap
.emplace(rSymbol
, rAddIn
).second
;
391 SAL_WARN_IF( !bOk
, "formula.core", "OpCodeMap::putExternal: symbol not inserted, " << rSymbol
<< " -> " << rAddIn
);
394 bOk
= maReverseExternalHashMap
.emplace(rAddIn
, rSymbol
).second
;
395 // Failed insertion of the AddIn is ok for different symbols mapping to
396 // the same AddIn. Make this INFO only.
397 SAL_INFO_IF( !bOk
, "formula.core", "OpCodeMap::putExternal: AddIn not inserted, " << rAddIn
<< " -> " << rSymbol
);
401 void FormulaCompiler::OpCodeMap::putExternalSoftly( const OUString
& rSymbol
, const OUString
& rAddIn
)
403 bool bOk
= maReverseExternalHashMap
.emplace(rAddIn
, rSymbol
).second
;
405 maExternalHashMap
.emplace(rSymbol
, rAddIn
);
408 uno::Sequence
< sheet::FormulaToken
> FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(
409 const FormulaCompiler
& rCompiler
, const uno::Sequence
< OUString
>& rNames
) const
411 const sal_Int32 nLen
= rNames
.getLength();
412 uno::Sequence
< sheet::FormulaToken
> aTokens( nLen
);
413 sheet::FormulaToken
* pToken
= aTokens
.getArray();
414 OUString
const * pName
= rNames
.getConstArray();
415 OUString
const * const pStop
= pName
+ nLen
;
416 for ( ; pName
< pStop
; ++pName
, ++pToken
)
418 OpCodeHashMap::const_iterator
iLook( maHashMap
.find( *pName
));
419 if (iLook
!= maHashMap
.end())
420 pToken
->OpCode
= (*iLook
).second
;
426 ExternalHashMap::const_iterator
iExt( maExternalHashMap
.find( *pName
));
427 if (iExt
!= maExternalHashMap
.end())
428 aIntName
= (*iExt
).second
;
429 // Check for existence not needed here, only name-mapping is of
432 if (aIntName
.isEmpty())
433 aIntName
= rCompiler
.FindAddInFunction(*pName
, !isEnglish()); // bLocalFirst=false for english
434 if (aIntName
.isEmpty())
435 pToken
->OpCode
= getOpCodeUnknown();
438 pToken
->OpCode
= ocExternal
;
439 pToken
->Data
<<= aIntName
;
446 uno::Sequence
< sheet::FormulaOpCodeMapEntry
> FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(
447 const FormulaCompiler
& rCompiler
, const sal_Int32 nGroups
) const
449 using namespace sheet
;
451 // Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
452 // we don't know in advance how many elements it will have we use a
453 // temporary vector to add elements and then copy to Sequence :-(
454 ::std::vector
< FormulaOpCodeMapEntry
> aVec
;
456 if (nGroups
== FormulaMapGroup::SPECIAL
)
458 // Use specific order, keep in sync with
459 // offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
465 { FormulaMapGroupSpecialOffset::PUSH
, ocPush
} ,
466 { FormulaMapGroupSpecialOffset::CALL
, ocCall
} ,
467 { FormulaMapGroupSpecialOffset::STOP
, ocStop
} ,
468 { FormulaMapGroupSpecialOffset::EXTERNAL
, ocExternal
} ,
469 { FormulaMapGroupSpecialOffset::NAME
, ocName
} ,
470 { FormulaMapGroupSpecialOffset::NO_NAME
, ocNoName
} ,
471 { FormulaMapGroupSpecialOffset::MISSING
, ocMissing
} ,
472 { FormulaMapGroupSpecialOffset::BAD
, ocBad
} ,
473 { FormulaMapGroupSpecialOffset::SPACES
, ocSpaces
} ,
474 { FormulaMapGroupSpecialOffset::MAT_REF
, ocMatRef
} ,
475 { FormulaMapGroupSpecialOffset::DB_AREA
, ocDBArea
} ,
476 /* TODO: { FormulaMapGroupSpecialOffset::TABLE_REF , ocTableRef } , */
477 { FormulaMapGroupSpecialOffset::MACRO
, ocMacro
} ,
478 { FormulaMapGroupSpecialOffset::COL_ROW_NAME
, ocColRowName
}
480 const size_t nCount
= SAL_N_ELEMENTS(aMap
);
481 // Preallocate vector elements.
482 FormulaOpCodeMapEntry aEntry
;
483 aEntry
.Token
.OpCode
= getOpCodeUnknown();
484 aVec
.resize(nCount
, aEntry
);
488 size_t nIndex
= static_cast< size_t >( i
.nOff
);
489 if (aVec
.size() <= nIndex
)
491 // The offsets really should be aligned with the size, so if
492 // the vector was preallocated above this code to resize it is
493 // just a measure in case the table isn't in sync with the API,
494 // usually it isn't executed.
495 aEntry
.Token
.OpCode
= getOpCodeUnknown();
496 aVec
.resize( nIndex
+ 1, aEntry
);
498 aEntry
.Token
.OpCode
= i
.eOp
;
499 aVec
[nIndex
] = aEntry
;
504 /* FIXME: Once we support error constants in formulas we'll need a map
505 * group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
506 * SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
508 // Anything else but SPECIAL.
509 if ((nGroups
& FormulaMapGroup::SEPARATORS
) != 0)
511 static const sal_uInt16 aOpCodes
[] = {
516 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
518 if ((nGroups
& FormulaMapGroup::ARRAY_SEPARATORS
) != 0)
520 static const sal_uInt16 aOpCodes
[] = {
521 SC_OPCODE_ARRAY_OPEN
,
522 SC_OPCODE_ARRAY_CLOSE
,
523 SC_OPCODE_ARRAY_ROW_SEP
,
524 SC_OPCODE_ARRAY_COL_SEP
526 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
528 if ((nGroups
& FormulaMapGroup::UNARY_OPERATORS
) != 0)
530 // Due to the nature of the percent operator following its operand
531 // it isn't sorted into unary operators for compiler interna.
532 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), ocPercentSign
);
533 // "+" can be used as unary operator too, push only if binary group is not set
534 if ((nGroups
& FormulaMapGroup::BINARY_OPERATORS
) == 0)
535 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), ocAdd
);
536 // regular unary operators
537 for (sal_uInt16 nOp
= SC_OPCODE_START_UN_OP
; nOp
< SC_OPCODE_STOP_UN_OP
&& nOp
< mnSymbols
; ++nOp
)
539 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
542 if ((nGroups
& FormulaMapGroup::BINARY_OPERATORS
) != 0)
544 for (sal_uInt16 nOp
= SC_OPCODE_START_BIN_OP
; nOp
< SC_OPCODE_STOP_BIN_OP
&& nOp
< mnSymbols
; ++nOp
)
548 // AND and OR in fact are functions but for legacy reasons
549 // are sorted into binary operators for compiler interna.
554 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
558 if ((nGroups
& FormulaMapGroup::FUNCTIONS
) != 0)
560 // Function names are not consecutive, skip the gaps between
561 // functions with no parameter, functions with 1 parameter
562 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), SC_OPCODE_START_NO_PAR
,
563 ::std::min
< sal_uInt16
>( SC_OPCODE_STOP_NO_PAR
, mnSymbols
) );
564 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), SC_OPCODE_START_1_PAR
,
565 ::std::min
< sal_uInt16
>( SC_OPCODE_STOP_1_PAR
, mnSymbols
) );
566 // Additional functions not within range of functions.
567 static const sal_uInt16 aOpCodes
[] = {
575 lclPushOpCodeMapEntries( aVec
, mpTable
.get(), aOpCodes
, SAL_N_ELEMENTS(aOpCodes
) );
576 // functions with 2 or more parameters.
577 for (sal_uInt16 nOp
= SC_OPCODE_START_2_PAR
; nOp
< SC_OPCODE_STOP_2_PAR
&& nOp
< mnSymbols
; ++nOp
)
581 // NO_NAME is in SPECIAL.
582 case SC_OPCODE_NO_NAME
:
585 lclPushOpCodeMapEntry( aVec
, mpTable
.get(), nOp
);
588 // If AddIn functions are present in this mapping, use them, and only those.
591 for (auto const& elem
: maExternalHashMap
)
593 FormulaOpCodeMapEntry aEntry
;
594 aEntry
.Name
= elem
.first
;
595 aEntry
.Token
.Data
<<= elem
.second
;
596 aEntry
.Token
.OpCode
= ocExternal
;
597 aVec
.push_back( aEntry
);
602 rCompiler
.fillAddInToken( aVec
, isEnglish());
606 return uno::Sequence
< FormulaOpCodeMapEntry
>(aVec
.data(), aVec
.size());
610 void FormulaCompiler::OpCodeMap::putOpCode( const OUString
& rStr
, const OpCode eOp
, const CharClass
* pCharClass
)
612 if (0 < eOp
&& sal_uInt16(eOp
) < mnSymbols
)
614 bool bPutOp
= mpTable
[eOp
].isEmpty();
615 bool bRemoveFromMap
= false;
620 // These OpCodes are meant to overwrite and also remove an
624 bRemoveFromMap
= true;
626 // These separator OpCodes are meant to overwrite and also
627 // remove an existing mapping if it is not used for one of the
631 bRemoveFromMap
= (mpTable
[ocArrayRowSep
] != mpTable
[eOp
] && mpTable
[ocSep
] != mpTable
[eOp
]);
635 bRemoveFromMap
= (mpTable
[ocArrayColSep
] != mpTable
[eOp
] && mpTable
[ocSep
] != mpTable
[eOp
]);
637 // For ocSep keep the ";" in map but remove any other if it is
638 // not used for ocArrayColSep or ocArrayRowSep.
641 bRemoveFromMap
= (mpTable
[eOp
] != ";" &&
642 mpTable
[ocArrayColSep
] != mpTable
[eOp
] &&
643 mpTable
[ocArrayRowSep
] != mpTable
[eOp
]);
645 // These OpCodes are known to be duplicates in the Excel
646 // external API mapping because of different parameter counts
647 // in different BIFF versions. Names are identical and entries
658 case ocGetDiffDate360
:
659 if (rStr
== mpTable
[eOp
])
662 // These OpCodes are known to be added to an existing mapping,
663 // but only for the OOXML external API mapping. This is *not*
664 // FormulaLanguage::OOXML. Keep the first
665 // (correct) definition for the OpCode, all following are
666 // additional alias entries in the map.
674 FormulaGrammar::extractFormulaLanguage( meGrammar
) == FormulaGrammar::GRAM_EXTERNAL
)
676 // Both bPutOp and bRemoveFromMap stay false.
681 SAL_WARN("formula.core",
682 "OpCodeMap::putOpCode: reusing OpCode " << static_cast<sal_uInt16
>(eOp
)
683 << ", replacing '" << mpTable
[eOp
] << "' with '" << rStr
<< "' in "
684 << (mbEnglish
? "" : "non-") << "English map 0x" << ::std::hex
<< meGrammar
);
688 // Case preserving opcode -> string, upper string -> opcode
691 OUString
aUpper( pCharClass
? pCharClass
->uppercase( mpTable
[eOp
]) : rStr
.toAsciiUpperCase());
692 // Ensure we remove a mapping only for the requested OpCode.
693 OpCodeHashMap::const_iterator
it( maHashMap
.find( aUpper
));
694 if (it
!= maHashMap
.end() && (*it
).second
== eOp
)
695 maHashMap
.erase( it
);
699 OUString
aUpper( pCharClass
? pCharClass
->uppercase( rStr
) : rStr
.toAsciiUpperCase());
700 maHashMap
.emplace(aUpper
, eOp
);
704 SAL_WARN( "formula.core", "OpCodeMap::putOpCode: OpCode out of range");
709 FormulaCompiler::FormulaCompiler( FormulaTokenArray
& rArr
, bool bComputeII
, bool bMatrixFlag
)
711 nCurrentFactorParam(0),
713 maArrIterator( rArr
),
718 nNumFmt( SvNumFormatType::UNDEFINED
),
720 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
721 bAutoCorrect( false ),
724 needsRPNTokenCheck( false ),
725 mbJumpCommandReorder(true),
727 mbComputeII(bComputeII
),
728 mbMatrixFlag(bMatrixFlag
)
732 FormulaTokenArray
FormulaCompiler::smDummyTokenArray
;
734 FormulaCompiler::FormulaCompiler(bool bComputeII
, bool bMatrixFlag
)
736 nCurrentFactorParam(0),
738 maArrIterator( smDummyTokenArray
),
743 nNumFmt( SvNumFormatType::UNDEFINED
),
745 meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED
),
746 bAutoCorrect( false ),
749 needsRPNTokenCheck( false ),
750 mbJumpCommandReorder(true),
752 mbComputeII(bComputeII
),
753 mbMatrixFlag(bMatrixFlag
)
757 FormulaCompiler::~FormulaCompiler()
761 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage
) const
763 FormulaCompiler::OpCodeMapPtr xMap
;
764 using namespace sheet
;
767 case FormulaLanguage::ODFF
:
770 xMap
= mxSymbolsODFF
;
772 case FormulaLanguage::ODF_11
:
775 xMap
= mxSymbolsPODF
;
777 case FormulaLanguage::ENGLISH
:
778 if (!mxSymbolsEnglish
)
779 InitSymbolsEnglish();
780 xMap
= mxSymbolsEnglish
;
782 case FormulaLanguage::NATIVE
:
783 if (!mxSymbolsNative
)
785 xMap
= mxSymbolsNative
;
787 case FormulaLanguage::XL_ENGLISH
:
788 if (!mxSymbolsEnglishXL
)
789 InitSymbolsEnglishXL();
790 xMap
= mxSymbolsEnglishXL
;
792 case FormulaLanguage::OOXML
:
795 xMap
= mxSymbolsOOXML
;
797 case FormulaLanguage::API
:
803 ; // nothing, NULL map returned
808 OUString
FormulaCompiler::FindAddInFunction( const OUString
& /*rUpperName*/, bool /*bLocalFirst*/ ) const
813 FormulaCompiler::OpCodeMapPtr
FormulaCompiler::CreateOpCodeMap(
815 const sheet::FormulaOpCodeMapEntry
> & rMapping
,
818 using sheet::FormulaOpCodeMapEntry
;
819 // Filter / API maps are never Core
820 NonConstOpCodeMapPtr xMap
= std::make_shared
<OpCodeMap
>( SC_OPCODE_LAST_OPCODE_ID
+ 1, false,
821 FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(
822 FormulaGrammar::GRAM_EXTERNAL
, bEnglish
), FormulaGrammar::CONV_UNSPECIFIED
));
823 std::unique_ptr
<CharClass
> xCharClass( xMap
->isEnglish() ? nullptr : createCharClassIfNonEnglishUI());
824 const CharClass
* pCharClass
= xCharClass
.get();
825 for (auto const& rMapEntry
: rMapping
)
827 OpCode eOp
= OpCode(rMapEntry
.Token
.OpCode
);
828 if (eOp
!= ocExternal
)
829 xMap
->putOpCode( rMapEntry
.Name
, eOp
, pCharClass
);
832 OUString aExternalName
;
833 if (rMapEntry
.Token
.Data
>>= aExternalName
)
834 xMap
->putExternal( rMapEntry
.Name
, aExternalName
);
837 SAL_WARN( "formula.core", "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
844 static void lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr
& xMap
, bool bDestroy
= false )
846 static OpCodeMapData aSymbolMap
;
847 osl::MutexGuard
aGuard(&aSymbolMap
.maMtx
);
851 aSymbolMap
.mxSymbolMap
.reset();
853 else if (!aSymbolMap
.mxSymbolMap
)
856 aSymbolMap
.mxSymbolMap
=
857 std::make_shared
<FormulaCompiler::OpCodeMap
>(
858 SC_OPCODE_LAST_OPCODE_ID
+ 1, true, FormulaGrammar::GRAM_NATIVE_UI
);
859 OpCodeList
aOpCodeListSymbols(false, RID_STRLIST_FUNCTION_NAMES_SYMBOLS
, aSymbolMap
.mxSymbolMap
);
860 OpCodeList
aOpCodeListNative(true, RID_STRLIST_FUNCTION_NAMES
, aSymbolMap
.mxSymbolMap
);
861 // No AddInMap for native core mapping.
864 xMap
= aSymbolMap
.mxSymbolMap
;
867 const OUString
& FormulaCompiler::GetNativeSymbol( OpCode eOp
)
869 NonConstOpCodeMapPtr xSymbolsNative
;
870 lcl_fillNativeSymbols( xSymbolsNative
);
871 return xSymbolsNative
->getSymbol( eOp
);
874 sal_Unicode
FormulaCompiler::GetNativeSymbolChar( OpCode eOp
)
876 return GetNativeSymbol(eOp
)[0];
879 void FormulaCompiler::InitSymbolsNative() const
881 lcl_fillNativeSymbols( mxSymbolsNative
);
884 void FormulaCompiler::InitSymbolsEnglish() const
886 static OpCodeMapData aMap
;
887 osl::MutexGuard
aGuard(&aMap
.maMtx
);
888 if (!aMap
.mxSymbolMap
)
889 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
890 mxSymbolsEnglish
= aMap
.mxSymbolMap
;
893 void FormulaCompiler::InitSymbolsPODF() const
895 static OpCodeMapData aMap
;
896 osl::MutexGuard
aGuard(&aMap
.maMtx
);
897 if (!aMap
.mxSymbolMap
)
898 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF
, FormulaGrammar::GRAM_PODF
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
899 mxSymbolsPODF
= aMap
.mxSymbolMap
;
902 void FormulaCompiler::InitSymbolsAPI() const
904 static OpCodeMapData aMap
;
905 osl::MutexGuard
aGuard(&aMap
.maMtx
);
906 if (!aMap
.mxSymbolMap
)
907 // XFunctionAccess API always used PODF grammar, keep it.
908 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API
, FormulaGrammar::GRAM_PODF
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
909 mxSymbolsAPI
= aMap
.mxSymbolMap
;
912 void FormulaCompiler::InitSymbolsODFF() const
914 static OpCodeMapData aMap
;
915 osl::MutexGuard
aGuard(&aMap
.maMtx
);
916 if (!aMap
.mxSymbolMap
)
917 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF
, FormulaGrammar::GRAM_ODFF
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
918 mxSymbolsODFF
= aMap
.mxSymbolMap
;
921 void FormulaCompiler::InitSymbolsEnglishXL() const
923 static OpCodeMapData aMap
;
924 osl::MutexGuard
aGuard(&aMap
.maMtx
);
925 if (!aMap
.mxSymbolMap
)
926 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH
, FormulaGrammar::GRAM_ENGLISH
, aMap
.mxSymbolMap
);
927 mxSymbolsEnglishXL
= aMap
.mxSymbolMap
;
929 // TODO: For now, just replace the separators to the Excel English
930 // variants. Later, if we want to properly map Excel functions with Calc
931 // functions, we'll need to do a little more work here.
932 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocSep
, nullptr);
933 mxSymbolsEnglishXL
->putOpCode( OUString(','), ocArrayColSep
, nullptr);
934 mxSymbolsEnglishXL
->putOpCode( OUString(';'), ocArrayRowSep
, nullptr);
937 void FormulaCompiler::InitSymbolsOOXML() const
939 static OpCodeMapData aMap
;
940 osl::MutexGuard
aGuard(&aMap
.maMtx
);
941 if (!aMap
.mxSymbolMap
)
942 loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML
, FormulaGrammar::GRAM_OOXML
, aMap
.mxSymbolMap
, SeparatorType::RESOURCE_BASE
);
943 mxSymbolsOOXML
= aMap
.mxSymbolMap
;
947 void FormulaCompiler::loadSymbols(const std::pair
<const char*, int>* pSymbols
, FormulaGrammar::Grammar eGrammar
,
948 NonConstOpCodeMapPtr
& rxMap
, SeparatorType eSepType
) const
954 rxMap
= std::make_shared
<OpCodeMap
>( SC_OPCODE_LAST_OPCODE_ID
+ 1, eGrammar
!= FormulaGrammar::GRAM_ODFF
, eGrammar
);
955 OpCodeList
aOpCodeList(false, pSymbols
, rxMap
, eSepType
);
957 fillFromAddInMap( rxMap
, eGrammar
);
958 // Fill from collection for AddIns not already present.
959 if ( FormulaGrammar::GRAM_ENGLISH
!= eGrammar
)
960 fillFromAddInCollectionUpperName( rxMap
);
962 fillFromAddInCollectionEnglishName( rxMap
);
965 void FormulaCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr
& /*xMap */) const
969 void FormulaCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr
& /*xMap */) const
973 void FormulaCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr
& /*xMap*/, FormulaGrammar::Grammar
/*_eGrammar */) const
977 OpCode
FormulaCompiler::GetEnglishOpCode( const OUString
& rName
) const
979 FormulaCompiler::OpCodeMapPtr xMap
= GetOpCodeMap( sheet::FormulaLanguage::ENGLISH
);
981 formula::OpCodeHashMap::const_iterator
iLook( xMap
->getHashMap().find( rName
) );
982 bool bFound
= (iLook
!= xMap
->getHashMap().end());
983 return bFound
? (*iLook
).second
: ocNone
;
986 bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp
)
998 // more than one parameters:
999 // ocIndirect otherwise would have to do
1000 // StopListening and StartListening on a reference for every
1001 // interpreted value.
1003 // ocOffset results in indirect references.
1005 // ocDebugVar shows internal value that may change as the internal state changes.
1016 bool FormulaCompiler::IsOpCodeJumpCommand( OpCode eOp
)
1031 // Remove quotes, escaped quotes are unescaped.
1032 bool FormulaCompiler::DeQuote( OUString
& rStr
)
1034 sal_Int32 nLen
= rStr
.getLength();
1035 if ( nLen
> 1 && rStr
[0] == '\'' && rStr
[ nLen
-1 ] == '\'' )
1037 rStr
= rStr
.copy( 1, nLen
-2 );
1038 rStr
= rStr
.replaceAll( "\\\'", "\'" );
1044 void FormulaCompiler::fillAddInToken(
1045 ::std::vector
< sheet::FormulaOpCodeMapEntry
>& /*_rVec*/,
1046 bool /*_bIsEnglish*/) const
1050 bool FormulaCompiler::IsMatrixFunction( OpCode eOpCode
)
1064 case ocModalValue_Multi
:
1069 // added to avoid warnings
1076 void FormulaCompiler::OpCodeMap::putCopyOpCode( const OUString
& rSymbol
, OpCode eOp
)
1078 SAL_WARN_IF( !mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty(), "formula.core",
1079 "OpCodeMap::putCopyOpCode: NOT replacing OpCode " << static_cast<sal_uInt16
>(eOp
)
1080 << " '" << mpTable
[eOp
] << "' with empty name!");
1081 if (!mpTable
[eOp
].isEmpty() && rSymbol
.isEmpty())
1082 maHashMap
.emplace(mpTable
[eOp
], eOp
);
1085 mpTable
[eOp
] = rSymbol
;
1086 maHashMap
.emplace(rSymbol
, eOp
);
1090 void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap
& r
)
1092 maHashMap
= OpCodeHashMap( mnSymbols
);
1094 sal_uInt16 n
= r
.getSymbolCount();
1095 SAL_WARN_IF( n
!= mnSymbols
, "formula.core",
1096 "OpCodeMap::copyFrom: unequal size, this: " << mnSymbols
<< " that: " << n
);
1100 // OpCode 0 (ocPush) should never be in a map.
1101 SAL_WARN_IF( !mpTable
[0].isEmpty() || !r
.mpTable
[0].isEmpty(), "formula.core",
1102 "OpCodeMap::copyFrom: OpCode 0 assigned, this: '"
1103 << mpTable
[0] << "' that: '" << r
.mpTable
[0] << "'");
1105 // For bOverrideKnownBad when copying from the English core map (ODF 1.1
1106 // and API) to the native map (UI "use English function names") replace the
1107 // known bad legacy function names with correct ones.
1109 FormulaGrammar::extractFormulaLanguage( meGrammar
) == sheet::FormulaLanguage::NATIVE
&&
1110 FormulaGrammar::extractFormulaLanguage( r
.meGrammar
) == sheet::FormulaLanguage::ENGLISH
)
1112 for (sal_uInt16 i
= 1; i
< n
; ++i
)
1115 OpCode eOp
= OpCode(i
);
1122 aSymbol
= "MULTIPLE.OPERATIONS";
1125 aSymbol
= r
.mpTable
[i
];
1127 putCopyOpCode( aSymbol
, eOp
);
1132 for (sal_uInt16 i
= 1; i
< n
; ++i
)
1134 OpCode eOp
= OpCode(i
);
1135 const OUString
& rSymbol
= r
.mpTable
[i
];
1136 putCopyOpCode( rSymbol
, eOp
);
1140 // TODO: maybe copy the external maps too?
1144 FormulaError
FormulaCompiler::GetErrorConstant( const OUString
& rName
) const
1146 FormulaError nError
= FormulaError::NONE
;
1147 OpCodeHashMap::const_iterator
iLook( mxSymbols
->getHashMap().find( rName
));
1148 if (iLook
!= mxSymbols
->getHashMap().end())
1150 switch ((*iLook
).second
)
1152 // Not all may make sense in a formula, but these we know as
1155 nError
= FormulaError::NoCode
;
1158 nError
= FormulaError::DivisionByZero
;
1161 nError
= FormulaError::NoValue
;
1164 nError
= FormulaError::NoRef
;
1167 nError
= FormulaError::NoName
;
1170 nError
= FormulaError::IllegalFPOperation
;
1173 nError
= FormulaError::NotAvailable
;
1181 // Per convention recognize detailed "#ERRxxx!" constants, always
1182 // untranslated. Error numbers are sal_uInt16 so at most 5 decimal
1184 if (rName
.startsWithIgnoreAsciiCase("#ERR") && rName
.getLength() <= 10 && rName
[rName
.getLength()-1] == '!')
1186 sal_uInt32 nErr
= rName
.copy( 4, rName
.getLength() - 5).toUInt32();
1187 if (0 < nErr
&& nErr
<= SAL_MAX_UINT16
&& isPublishedFormulaError(static_cast<FormulaError
>(nErr
)))
1188 nError
= static_cast<FormulaError
>(nErr
);
1194 void FormulaCompiler::EnableJumpCommandReorder( bool bEnable
)
1196 mbJumpCommandReorder
= bEnable
;
1199 void FormulaCompiler::EnableStopOnError( bool bEnable
)
1201 mbStopOnError
= bEnable
;
1204 void FormulaCompiler::AppendErrorConstant( OUStringBuffer
& rBuffer
, FormulaError nError
) const
1209 case FormulaError::NoCode
:
1212 case FormulaError::DivisionByZero
:
1215 case FormulaError::NoValue
:
1218 case FormulaError::NoRef
:
1221 case FormulaError::NoName
:
1224 case FormulaError::IllegalFPOperation
:
1227 case FormulaError::NotAvailable
:
1232 // Per convention create detailed "#ERRxxx!" constants, always
1234 rBuffer
.append("#ERR");
1235 rBuffer
.append(static_cast<sal_Int32
>(nError
));
1236 rBuffer
.append('!');
1240 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
1243 constexpr short nRecursionMax
= 100;
1245 bool FormulaCompiler::GetToken()
1247 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
1248 if ( nRecursion
> nRecursionMax
)
1250 SetError( FormulaError::StackOverflow
);
1251 mpLastToken
= mpToken
= new FormulaByteToken( ocStop
);
1254 if ( bAutoCorrect
&& !pStack
)
1255 { // don't merge stacked subroutine code into entered formula
1256 aCorrectedFormula
+= aCorrectedSymbol
;
1257 aCorrectedSymbol
.clear();
1260 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
1264 FormulaTokenRef pSpacesToken
;
1265 short nWasColRowName
;
1266 if ( pArr
->OpCodeBefore( maArrIterator
.GetIndex() ) == ocColRowName
)
1270 mpToken
= maArrIterator
.Next();
1271 while( mpToken
&& mpToken
->GetOpCode() == ocSpaces
)
1273 // For significant whitespace remember last ocSpaces token. Usually
1274 // there's only one even for multiple spaces.
1275 pSpacesToken
= mpToken
;
1276 if ( nWasColRowName
)
1278 if ( bAutoCorrect
&& !pStack
)
1279 CreateStringFromToken( aCorrectedFormula
, mpToken
.get() );
1280 mpToken
= maArrIterator
.Next();
1282 if ( bAutoCorrect
&& !pStack
&& mpToken
)
1283 CreateStringFromToken( aCorrectedSymbol
, mpToken
.get() );
1289 // mpLastToken was popped as well and corresponds to the
1290 // then current last token during PushTokenArray(), e.g. for
1299 if ( nWasColRowName
>= 2 && mpToken
->GetOpCode() == ocColRowName
)
1300 { // convert an ocSpaces to ocIntersect in RPN
1301 mpLastToken
= mpToken
= new FormulaByteToken( ocIntersect
);
1302 maArrIterator
.StepBack(); // we advanced to the second ocColRowName, step back
1304 else if (pSpacesToken
&& FormulaGrammar::isExcelSyntax( meGrammar
) &&
1305 mpLastToken
&& mpToken
&&
1306 isPotentialRangeType( mpLastToken
.get(), false, false) &&
1307 isPotentialRangeType( mpToken
.get(), false, true))
1309 // Let IntersectionLine() <- Factor() decide how to treat this,
1310 // once the actual arguments are determined in RPN.
1311 mpLastToken
= mpToken
= pSpacesToken
;
1312 maArrIterator
.StepBack(); // step back from next non-spaces token
1319 mpLastToken
= mpToken
= new FormulaByteToken( ocStop
);
1323 // Remember token for next round and any PushTokenArray() calls that may
1324 // occur in handlers.
1325 mpLastToken
= mpToken
;
1327 if ( mpToken
->IsExternalRef() )
1329 return HandleExternalReference(*mpToken
);
1333 switch (mpToken
->GetOpCode())
1342 // Expanding ocName might have introduced tokens such as ocStyle that prevent formula threading,
1343 // but those wouldn't be present in the raw tokens array, so ensure RPN tokens will be checked too.
1344 needsRPNTokenCheck
= true;
1349 return HandleColRowName();
1351 return HandleDbData();
1353 return HandleTableRef();
1356 HandleIIOpCode(mpToken
.get(), nullptr, 0);
1366 // RPN creation by recursion
1367 void FormulaCompiler::Factor()
1369 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
1372 CurrentFactor
pFacToken( this );
1374 OpCode eOp
= mpToken
->GetOpCode();
1375 if (eOp
== ocPush
|| eOp
== ocColRowNameAuto
|| eOp
== ocMatRef
|| eOp
== ocDBArea
1376 || eOp
== ocTableRef
1377 || (!mbJumpCommandReorder
&& ((eOp
== ocName
) || (eOp
== ocColRowName
) || (eOp
== ocBad
)))
1384 // PUSH( is an error that may be caused by an unknown function.
1386 ( mpToken
->GetType() == svString
1387 || mpToken
->GetType() == svSingleRef
)
1388 ? FormulaError::NoName
: FormulaError::OperatorExpected
);
1389 if ( bAutoCorrect
&& !pStack
)
1390 { // assume multiplication
1391 aCorrectedFormula
+= mxSymbols
->getSymbol( ocMul
);
1395 if( eOp
!= ocClose
)
1396 SetError( FormulaError::PairExpected
);
1402 else if( eOp
== ocOpen
)
1406 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1407 { // range list (A1;A2) converted to (A1~A2)
1408 pFacToken
= mpToken
;
1410 CheckSetForceArrayParameter( mpToken
, 0);
1412 // Do not ignore error here, regardless of mbStopOnError, to not
1413 // change the formula expression in case of an unexpected state.
1414 if (pArr
->GetCodeError() == FormulaError::NONE
&& pc
>= 2)
1416 // Left and right operands must be reference or function
1417 // returning reference to form a range list.
1418 const FormulaToken
* p
= pCode
[-2];
1419 if (p
&& isPotentialRangeType( p
, true, false))
1422 if (p
&& isPotentialRangeType( p
, true, true))
1424 pFacToken
->NewOpCode( ocUnion
, FormulaToken::PrivateAccess());
1425 // XXX NOTE: the token's eType is still svSep here!
1426 PutCode( pFacToken
);
1432 SetError( FormulaError::PairExpected
);
1436 /* TODO: if no conversion to ocUnion is involved this could collect
1437 * such expression as a list or (matrix) vector to be passed as
1438 * argument for one parameter (which in fact the ocUnion svRefList is a
1439 * special case of), which would require a new StackVar type and needed
1440 * to be handled by the interpreter for functions that could support it
1441 * (i.e. already handle VAR_ARGS or svRefList parameters). This is also
1442 * not defined by ODF.
1443 * Does Excel handle =SUM((1;2))?
1444 * As is, the interpreter catches extraneous uncalculated
1445 * subexpressions like 1 of (1;2) as error. */
1449 if( nNumFmt
== SvNumFormatType::UNDEFINED
)
1450 nNumFmt
= lcl_GetRetFormat( eOp
);
1452 if ( IsOpCodeVolatile( eOp
) )
1453 pArr
->SetExclusiveRecalcModeAlways();
1458 // Functions recalculated on every document load.
1459 // ONLOAD_LENIENT here to be able to distinguish and not
1460 // force a recalc (if not in an ALWAYS or ONLOAD_MUST
1461 // context) but keep an imported result from for example
1462 // OOXML a DDE call. Will be recalculated for ODFF.
1467 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT
);
1469 // RANDBETWEEN() is volatile like RAND(). Other Add-In
1470 // functions may have to be recalculated or not, we don't
1471 // know, classify as ONLOAD_LENIENT.
1473 if (mpToken
->GetExternal() == "com.sun.star.sheet.addin.Analysis.getRandbetween")
1474 pArr
->SetExclusiveRecalcModeAlways();
1476 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT
);
1478 // If the referred cell is moved the value changes.
1481 pArr
->SetRecalcModeOnRefMove();
1483 // ocCell needs recalc on move for some possible type values.
1484 // And recalc mode on load, tdf#60645
1486 pArr
->SetRecalcModeOnRefMove();
1487 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_MUST
);
1490 // Cell with hyperlink needs to be calculated on load to
1491 // get its matrix result generated.
1492 pArr
->AddRecalcMode( ScRecalcMode::ONLOAD_MUST
);
1493 pArr
->SetHyperLink( true);
1499 if (SC_OPCODE_START_NO_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_NO_PAR
)
1501 pFacToken
= mpToken
;
1505 SetError( FormulaError::PairExpected
);
1506 PutCode( pFacToken
);
1512 SetError( FormulaError::PairExpected
);
1513 PutCode( pFacToken
);
1517 else if (SC_OPCODE_START_1_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_1_PAR
)
1519 if (eOp
== ocIsoWeeknum
&& FormulaGrammar::isODFF( meGrammar
))
1521 // tdf#50950 ocIsoWeeknum can have 2 arguments when saved by older versions of Calc;
1522 // the opcode then has to be changed to ocWeek for backward compatibility
1523 pFacToken
= mpToken
;
1525 bool bNoParam
= false;
1533 CheckSetForceArrayParameter( mpToken
, 0);
1538 SetError( FormulaError::PairExpected
);
1539 sal_uInt32 nSepCount
= 0;
1540 const sal_uInt16 nSepPos
= maArrIterator
.GetIndex() - 1; // separator position, if any
1544 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1547 CheckSetForceArrayParameter( mpToken
, nSepCount
);
1549 if (nSepCount
> FORMULA_MAXPARAMS
)
1550 SetError( FormulaError::CodeOverflow
);
1555 SetError( FormulaError::PairExpected
);
1558 pFacToken
->SetByte( nSepCount
);
1561 // An old mode!=1 indicates ISO week, remove argument if
1562 // literal double value and keep function. Anything else
1563 // can not be resolved, there exists no "like ISO but week
1564 // starts on Sunday" mode in WEEKNUM and for an expression
1565 // we can't determine.
1566 // Current index is nSepPos+3 if expression stops, or
1567 // nSepPos+4 if expression continues after the call because
1568 // we just called NextToken() to move away from it.
1569 if (pc
>= 2 && (maArrIterator
.GetIndex() == nSepPos
+ 3 || maArrIterator
.GetIndex() == nSepPos
+ 4) &&
1570 pArr
->TokenAt(nSepPos
+1)->GetType() == svDouble
&&
1571 pArr
->TokenAt(nSepPos
+1)->GetDouble() != 1.0 &&
1572 pArr
->TokenAt(nSepPos
+2)->GetOpCode() == ocClose
&&
1573 pArr
->RemoveToken( nSepPos
, 2) == 2)
1575 maArrIterator
.AfterRemoveToken( nSepPos
, 2);
1576 // Remove the ocPush/svDouble just removed also from
1577 // the compiler local RPN array.
1579 (*pCode
)->DecRef(); // may be dead now
1580 pFacToken
->SetByte( nSepCount
- 1 );
1584 // For the remaining two arguments cases use the
1585 // compatibility function.
1586 pFacToken
->NewOpCode( ocWeeknumOOo
, FormulaToken::PrivateAccess());
1589 PutCode( pFacToken
);
1593 // standard handling of 1-parameter opcodes
1594 pFacToken
= mpToken
;
1596 if( nNumFmt
== SvNumFormatType::UNDEFINED
&& eOp
== ocNot
)
1597 nNumFmt
= SvNumFormatType::LOGICAL
;
1601 CheckSetForceArrayParameter( mpToken
, 0);
1605 SetError( FormulaError::PairExpected
);
1607 SetError( FormulaError::PairExpected
);
1608 else if ( pArr
->GetCodeError() == FormulaError::NONE
)
1610 pFacToken
->SetByte( 1 );
1613 FormulaToken
** pArg
= pCode
- 1;
1614 HandleIIOpCode(pFacToken
, &pArg
, 1);
1617 PutCode( pFacToken
);
1621 else if ((SC_OPCODE_START_2_PAR
<= eOp
&& eOp
< SC_OPCODE_STOP_2_PAR
)
1622 || eOp
== ocExternal
1627 || ( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
1628 || (!mbJumpCommandReorder
&& IsOpCodeJumpCommand(eOp
)))
1630 pFacToken
= mpToken
;
1631 OpCode eMyLastOp
= eOp
;
1633 bool bNoParam
= false;
1634 bool bBadName
= false;
1642 CheckSetForceArrayParameter( mpToken
, 0);
1646 else if (eMyLastOp
== ocBad
)
1648 // Just a bad name, not an unknown function, no parameters, no
1649 // closing expected.
1654 SetError( FormulaError::PairExpected
);
1655 sal_uInt32 nSepCount
= 0;
1658 bool bDoIICompute
= mbComputeII
;
1659 // Array of FormulaToken double pointers to collect the parameters of II opcodes.
1660 FormulaToken
*** pArgArray
= nullptr;
1663 pArgArray
= static_cast<FormulaToken
***>(alloca(sizeof(FormulaToken
**)*FORMULA_MAXPARAMSII
));
1665 bDoIICompute
= false;
1671 pArgArray
[nSepCount
-1] = pCode
- 1; // Add first argument
1673 while ((eOp
== ocSep
) && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1676 CheckSetForceArrayParameter( mpToken
, nSepCount
);
1678 if (nSepCount
> FORMULA_MAXPARAMS
)
1679 SetError( FormulaError::CodeOverflow
);
1681 if (bDoIICompute
&& nSepCount
<= FORMULA_MAXPARAMSII
)
1682 pArgArray
[nSepCount
- 1] = pCode
- 1; // Add rest of the arguments
1685 HandleIIOpCode(pFacToken
, pArgArray
,
1686 std::min(nSepCount
, static_cast<sal_uInt32
>(FORMULA_MAXPARAMSII
)));
1690 ; // nothing, keep current token for return
1691 else if (eOp
!= ocClose
)
1692 SetError( FormulaError::PairExpected
);
1698 // Jumps are just normal functions for the FunctionAutoPilot tree view
1699 if (!mbJumpCommandReorder
&& pFacToken
->GetType() == svJump
)
1700 pFacToken
= new FormulaFAPToken( pFacToken
->GetOpCode(), nSepCount
, pFacToken
);
1702 pFacToken
->SetByte( nSepCount
);
1703 PutCode( pFacToken
);
1708 else if (IsOpCodeJumpCommand(eOp
))
1710 // the PC counters are -1
1711 pFacToken
= mpToken
;
1715 pFacToken
->GetJump()[ 0 ] = 3; // if, else, behind
1718 pFacToken
->GetJump()[ 0 ] = FORMULA_MAXJUMPCOUNT
+ 1;
1722 pFacToken
->GetJump()[ 0 ] = 2; // if, behind
1725 SAL_WARN("formula.core","Jump OpCode: " << +eOp
);
1726 assert(!"FormulaCompiler::Factor: someone forgot to add a jump count case");
1732 CheckSetForceArrayParameter( mpToken
, 0);
1736 SetError( FormulaError::PairExpected
);
1737 PutCode( pFacToken
);
1738 // During AutoCorrect (since pArr->GetCodeError() is
1739 // ignored) an unlimited ocIf would crash because
1740 // ScRawToken::Clone() allocates the JumpBuffer according to
1741 // nJump[0]*2+2, which is 3*2+2 on ocIf and 2*2+2 ocIfError and ocIfNA.
1743 OpCode eFacOpCode
= pFacToken
->GetOpCode();
1750 nJumpMax
= FORMULA_MAXJUMPCOUNT
;
1758 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode
);
1759 assert(!"FormulaCompiler::Factor: someone forgot to add a jump max case");
1761 short nJumpCount
= 0;
1762 while ( (nJumpCount
< (FORMULA_MAXJUMPCOUNT
- 1)) && (eOp
== ocSep
)
1763 && (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
))
1765 if ( ++nJumpCount
<= nJumpMax
)
1766 pFacToken
->GetJump()[nJumpCount
] = pc
-1;
1768 CheckSetForceArrayParameter( mpToken
, nJumpCount
- 1);
1770 // ocSep or ocClose terminate the subexpression
1774 SetError( FormulaError::PairExpected
);
1778 // always limit to nJumpMax, no arbitrary overwrites
1779 if ( ++nJumpCount
<= nJumpMax
)
1780 pFacToken
->GetJump()[ nJumpCount
] = pc
-1;
1781 eFacOpCode
= pFacToken
->GetOpCode();
1786 bLimitOk
= (nJumpCount
<= 3);
1789 bLimitOk
= (nJumpCount
< FORMULA_MAXJUMPCOUNT
);
1793 bLimitOk
= (nJumpCount
<= 2);
1797 SAL_WARN("formula.core","Jump OpCode: " << +eFacOpCode
);
1798 assert(!"FormulaCompiler::Factor: someone forgot to add a jump limit case");
1801 pFacToken
->GetJump()[ 0 ] = nJumpCount
;
1803 SetError( FormulaError::IllegalParameter
);
1806 else if ( eOp
== ocMissing
)
1811 else if ( eOp
== ocClose
)
1813 SetError( FormulaError::ParameterExpected
);
1815 else if ( eOp
== ocSep
)
1816 { // Subsequent ocSep
1817 SetError( FormulaError::ParameterExpected
);
1818 if ( bAutoCorrect
&& !pStack
)
1820 aCorrectedSymbol
.clear();
1824 else if ( mpToken
->IsExternalRef() )
1831 SetError( FormulaError::UnknownToken
);
1832 if ( bAutoCorrect
&& !pStack
)
1834 if ( eOp
== ocStop
)
1835 { // trailing operator w/o operand
1836 sal_Int32 nLen
= aCorrectedFormula
.getLength();
1838 aCorrectedFormula
= aCorrectedFormula
.copy( 0, nLen
- 1 );
1839 aCorrectedSymbol
.clear();
1847 void FormulaCompiler::RangeLine()
1850 while (mpToken
->GetOpCode() == ocRange
)
1852 FormulaToken
** pCode1
= pCode
- 1;
1853 FormulaTokenRef p
= mpToken
;
1856 FormulaToken
** pCode2
= pCode
- 1;
1857 if (!MergeRangeReference( pCode1
, pCode2
))
1862 void FormulaCompiler::IntersectionLine()
1865 while (mpToken
->GetOpCode() == ocIntersect
|| mpToken
->GetOpCode() == ocSpaces
)
1867 sal_uInt16 nCodeIndex
= maArrIterator
.GetIndex() - 1;
1868 FormulaToken
** pCode1
= pCode
- 1;
1869 FormulaTokenRef p
= mpToken
;
1872 FormulaToken
** pCode2
= pCode
- 1;
1873 if (p
->GetOpCode() == ocSpaces
)
1875 // Convert to intersection if both left and right are references or
1876 // functions (potentially returning references, if not then a space
1877 // or no space would be a syntax error anyway), not other operators
1878 // or operands. Else discard.
1879 if (isAdjacentOrGapRpnEnd( pc
, pCode
, pCode1
, pCode2
) && isIntersectable( pCode1
, pCode2
))
1881 FormulaTokenRef
pIntersect( new FormulaByteToken( ocIntersect
));
1882 // Replace ocSpaces with ocIntersect so that when switching
1883 // formula syntax the correct operator string is created.
1884 pArr
->ReplaceToken( nCodeIndex
, pIntersect
.get(), FormulaTokenArray::ReplaceMode::CODE_ONLY
);
1885 PutCode( pIntersect
);
1895 void FormulaCompiler::UnionLine()
1898 while (mpToken
->GetOpCode() == ocUnion
)
1900 FormulaTokenRef p
= mpToken
;
1907 void FormulaCompiler::UnaryLine()
1909 if( mpToken
->GetOpCode() == ocAdd
)
1911 else if (SC_OPCODE_START_UN_OP
<= mpToken
->GetOpCode() &&
1912 mpToken
->GetOpCode() < SC_OPCODE_STOP_UN_OP
)
1914 FormulaTokenRef p
= mpToken
;
1919 FormulaToken
** pArg
= pCode
- 1;
1920 HandleIIOpCode(p
.get(), &pArg
, 1);
1928 void FormulaCompiler::PostOpLine()
1931 while ( mpToken
->GetOpCode() == ocPercentSign
)
1932 { // this operator _follows_ its operand
1935 FormulaToken
** pArg
= pCode
- 1;
1936 HandleIIOpCode(mpToken
.get(), &pArg
, 1);
1943 void FormulaCompiler::PowLine()
1946 while (mpToken
->GetOpCode() == ocPow
)
1948 FormulaTokenRef p
= mpToken
;
1949 FormulaToken
** pArgArray
[2];
1951 pArgArray
[0] = pCode
- 1; // Add first argument
1956 pArgArray
[1] = pCode
- 1; // Add second argument
1957 HandleIIOpCode(p
.get(), pArgArray
, 2);
1963 void FormulaCompiler::MulDivLine()
1966 while (mpToken
->GetOpCode() == ocMul
|| mpToken
->GetOpCode() == ocDiv
)
1968 FormulaTokenRef p
= mpToken
;
1969 FormulaToken
** pArgArray
[2];
1971 pArgArray
[0] = pCode
- 1; // Add first argument
1976 pArgArray
[1] = pCode
- 1; // Add second argument
1977 HandleIIOpCode(p
.get(), pArgArray
, 2);
1983 void FormulaCompiler::AddSubLine()
1986 while (mpToken
->GetOpCode() == ocAdd
|| mpToken
->GetOpCode() == ocSub
)
1988 FormulaTokenRef p
= mpToken
;
1989 FormulaToken
** pArgArray
[2];
1991 pArgArray
[0] = pCode
- 1; // Add first argument
1996 pArgArray
[1] = pCode
- 1; // Add second argument
1997 HandleIIOpCode(p
.get(), pArgArray
, 2);
2003 void FormulaCompiler::ConcatLine()
2006 while (mpToken
->GetOpCode() == ocAmpersand
)
2008 FormulaTokenRef p
= mpToken
;
2009 FormulaToken
** pArgArray
[2];
2011 pArgArray
[0] = pCode
- 1; // Add first argument
2016 pArgArray
[1] = pCode
- 1; // Add second argument
2017 HandleIIOpCode(p
.get(), pArgArray
, 2);
2023 void FormulaCompiler::CompareLine()
2026 while (mpToken
->GetOpCode() >= ocEqual
&& mpToken
->GetOpCode() <= ocGreaterEqual
)
2028 FormulaTokenRef p
= mpToken
;
2029 FormulaToken
** pArgArray
[2];
2031 pArgArray
[0] = pCode
- 1; // Add first argument
2036 pArgArray
[1] = pCode
- 1; // Add second argument
2037 HandleIIOpCode(p
.get(), pArgArray
, 2);
2043 OpCode
FormulaCompiler::Expression()
2045 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
2046 if ( nRecursion
> nRecursionMax
)
2048 SetError( FormulaError::StackOverflow
);
2049 return ocStop
; //! generate token instead?
2052 while (mpToken
->GetOpCode() == ocAnd
|| mpToken
->GetOpCode() == ocOr
)
2054 FormulaTokenRef p
= mpToken
;
2055 mpToken
->SetByte( 2 ); // 2 parameters!
2056 FormulaToken
** pArgArray
[2];
2058 pArgArray
[0] = pCode
- 1; // Add first argument
2063 pArgArray
[1] = pCode
- 1; // Add second argument
2064 HandleIIOpCode(p
.get(), pArgArray
, 2);
2068 return mpToken
->GetOpCode();
2072 void FormulaCompiler::SetError( FormulaError
/*nError*/ )
2076 FormulaTokenRef
FormulaCompiler::ExtendRangeReference( FormulaToken
& /*rTok1*/, FormulaToken
& /*rTok2*/ )
2078 return FormulaTokenRef();
2081 bool FormulaCompiler::MergeRangeReference( FormulaToken
* * const pCode1
, FormulaToken
* const * const pCode2
)
2083 if (!isAdjacentRpnEnd( pc
, pCode
, pCode1
, pCode2
))
2086 FormulaToken
*p1
= *pCode1
, *p2
= *pCode2
;
2087 FormulaTokenRef p
= ExtendRangeReference( *p1
, *p2
);
2101 bool FormulaCompiler::CompileTokenArray()
2105 needsRPNTokenCheck
= false;
2106 if (pArr
->GetCodeError() == FormulaError::NONE
|| !mbStopOnError
)
2110 aCorrectedFormula
.clear();
2111 aCorrectedSymbol
.clear();
2114 maArrIterator
.Reset();
2116 FormulaToken
* pDataArray
[ FORMULA_MAXTOKENS
+ 1 ];
2117 // Code in some places refers to the last token as 'pCode - 1', which may
2118 // point before the first element if the expression is bad. So insert a dummy
2119 // node in that place which will make that token be nullptr.
2120 pDataArray
[ 0 ] = nullptr;
2121 FormulaToken
** pData
= pDataArray
+ 1;
2123 bool bWasForced
= pArr
->IsRecalcModeForced();
2124 if ( bWasForced
&& bAutoCorrect
)
2125 aCorrectedFormula
= "=";
2126 pArr
->ClearRecalcMode();
2127 maArrIterator
.Reset();
2131 OpCode eOp
= Expression();
2132 // Some trailing garbage that doesn't form an expression?
2134 SetError( FormulaError::OperatorExpected
);
2137 FormulaError nErrorBeforePop
= pArr
->GetCodeError();
2143 pArr
->CreateNewRPNArrayFromData( pData
, pc
);
2144 if( needsRPNTokenCheck
)
2145 pArr
->CheckAllRPNTokens();
2148 // once an error, always an error
2149 if( pArr
->GetCodeError() == FormulaError::NONE
&& nErrorBeforePop
!= FormulaError::NONE
)
2150 pArr
->SetCodeError( nErrorBeforePop
);
2152 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbStopOnError
)
2155 maArrIterator
.Reset();
2156 pArr
->SetHyperLink( false);
2160 pArr
->SetRecalcModeForced();
2162 if( nNumFmt
== SvNumFormatType::UNDEFINED
)
2163 nNumFmt
= SvNumFormatType::NUMBER
;
2167 void FormulaCompiler::PopTokenArray()
2172 FormulaArrayStack
* p
= pStack
;
2174 // obtain special RecalcMode from SharedFormula
2175 if ( pArr
->IsRecalcModeAlways() )
2176 p
->pArr
->SetExclusiveRecalcModeAlways();
2177 else if ( !pArr
->IsRecalcModeNormal() && p
->pArr
->IsRecalcModeNormal() )
2178 p
->pArr
->SetMaskedRecalcMode( pArr
->GetRecalcMode() );
2179 p
->pArr
->SetCombinedBitsRecalcMode( pArr
->GetRecalcMode() );
2180 if ( pArr
->IsHyperLink() ) // fdo 87534
2181 p
->pArr
->SetHyperLink( true );
2185 maArrIterator
= FormulaTokenArrayPlainIterator(*pArr
);
2186 maArrIterator
.Jump(p
->nIndex
);
2187 mpLastToken
= p
->mpLastToken
;
2191 void FormulaCompiler::CreateStringFromTokenArray( OUString
& rFormula
)
2193 OUStringBuffer
aBuffer( pArr
->GetLen() * 5 );
2194 CreateStringFromTokenArray( aBuffer
);
2195 rFormula
= aBuffer
.makeStringAndClear();
2198 void FormulaCompiler::CreateStringFromTokenArray( OUStringBuffer
& rBuffer
)
2200 rBuffer
.setLength(0);
2201 if( !pArr
->GetLen() )
2204 FormulaTokenArray
* pSaveArr
= pArr
;
2205 int nSaveIndex
= maArrIterator
.GetIndex();
2206 bool bODFF
= FormulaGrammar::isODFF( meGrammar
);
2207 if (bODFF
|| FormulaGrammar::isPODF( meGrammar
) )
2209 // Scan token array for missing args and re-write if present.
2210 MissingConventionODF
aConv( bODFF
);
2211 if (pArr
->NeedsPodfRewrite( aConv
))
2213 pArr
= pArr
->RewriteMissing( aConv
);
2214 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2217 else if ( FormulaGrammar::isOOXML( meGrammar
) )
2219 // Scan token array for missing args and rewrite if present.
2220 if (pArr
->NeedsOoxmlRewrite())
2222 MissingConventionOOXML aConv
;
2223 pArr
= pArr
->RewriteMissing( aConv
);
2224 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2228 // At least one character per token, plus some are references, some are
2229 // function names, some are numbers, ...
2230 rBuffer
.ensureCapacity( pArr
->GetLen() * 5 );
2232 if ( pArr
->IsRecalcModeForced() )
2233 rBuffer
.append( '=');
2234 const FormulaToken
* t
= maArrIterator
.First();
2236 t
= CreateStringFromToken( rBuffer
, t
, true );
2238 if (pSaveArr
!= pArr
)
2242 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2243 maArrIterator
.Jump(nSaveIndex
);
2247 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUString
& rFormula
, const FormulaToken
* pTokenP
)
2249 OUStringBuffer aBuffer
;
2250 const FormulaToken
* p
= CreateStringFromToken( aBuffer
, pTokenP
);
2251 rFormula
+= aBuffer
;
2255 const FormulaToken
* FormulaCompiler::CreateStringFromToken( OUStringBuffer
& rBuffer
, const FormulaToken
* pTokenP
,
2256 bool bAllowArrAdvance
)
2259 bool bSpaces
= false;
2260 const FormulaToken
* t
= pTokenP
;
2261 OpCode eOp
= t
->GetOpCode();
2262 if( eOp
>= ocAnd
&& eOp
<= ocOr
)
2265 if ( bAllowArrAdvance
)
2266 t
= maArrIterator
.Next();
2268 t
= maArrIterator
.PeekNext();
2270 bSpaces
= ( !t
|| t
->GetOpCode() != ocOpen
);
2273 rBuffer
.append( ' ');
2275 if( eOp
== ocSpaces
)
2277 bool bWriteSpaces
= true;
2278 if (mxSymbols
->isODFF())
2280 const FormulaToken
* p
= maArrIterator
.PeekPrevNoSpaces();
2281 bool bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
2282 if (bIntersectionOp
)
2284 p
= maArrIterator
.PeekNextNoSpaces();
2285 bIntersectionOp
= (p
&& p
->GetOpCode() == ocColRowName
);
2287 if (bIntersectionOp
)
2289 rBuffer
.append( "!!");
2290 bWriteSpaces
= false;
2295 // ODF v1.3 OpenFormula 5.14 Whitespace states "whitespace shall
2296 // not separate a function name from its initial opening
2299 // ECMA-376-1:2016 18.17.2 Syntax states "that no space characters
2300 // shall separate a function-name from the left parenthesis (()
2301 // that follows it." and Excel even chokes on it.
2303 // Suppress/remove it in any case also in UI, it will not be
2305 const FormulaToken
* p
= maArrIterator
.PeekPrevNoSpaces();
2306 if (p
&& p
->IsFunction())
2308 p
= maArrIterator
.PeekNextNoSpaces();
2309 if (p
&& p
->GetOpCode() == ocOpen
)
2310 bWriteSpaces
= false;
2315 // most times it's just one blank
2316 sal_uInt8 n
= t
->GetByte();
2317 for ( sal_uInt8 j
=0; j
<n
; ++j
)
2319 rBuffer
.append( ' ');
2323 else if( eOp
>= ocInternalBegin
&& eOp
<= ocInternalEnd
)
2324 rBuffer
.appendAscii( pInternal
[ eOp
- ocInternalBegin
] );
2325 else if (eOp
== ocIntersect
)
2327 // Nasty, ugly, horrific, terrifying...
2328 if (FormulaGrammar::isExcelSyntax( meGrammar
))
2329 rBuffer
.append(' ');
2331 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
2333 else if( static_cast<sal_uInt16
>(eOp
) < mxSymbols
->getSymbolCount()) // Keyword:
2334 rBuffer
.append( mxSymbols
->getSymbol( eOp
));
2337 SAL_WARN( "formula.core","unknown OpCode");
2338 rBuffer
.append( GetNativeSymbol( ocErrName
));
2342 if (t
->IsExternalRef())
2344 CreateStringFromExternal( rBuffer
, pTokenP
);
2348 switch( t
->GetType() )
2351 AppendDouble( rBuffer
, t
->GetDouble() );
2355 if( eOp
== ocBad
|| eOp
== ocStringXML
)
2356 rBuffer
.append( t
->GetString().getString());
2358 AppendString( rBuffer
, t
->GetString().getString() );
2361 CreateStringFromSingleRef( rBuffer
, t
);
2364 CreateStringFromDoubleRef( rBuffer
, t
);
2368 CreateStringFromMatrix( rBuffer
, t
);
2372 CreateStringFromIndex( rBuffer
, t
);
2373 if (t
->GetOpCode() == ocTableRef
&& bAllowArrAdvance
&& NeedsTableRefTransformation())
2375 // Suppress all TableRef related tokens, the resulting
2376 // range was written by CreateStringFromIndex().
2377 const FormulaToken
* const p
= maArrIterator
.PeekNext();
2378 if (p
&& p
->GetOpCode() == ocTableRefOpen
)
2383 t
= maArrIterator
.Next();
2387 // Switch cases correspond with those in
2388 // ScCompiler::HandleTableRef()
2389 switch (t
->GetOpCode())
2391 case ocTableRefOpen
:
2394 case ocTableRefClose
:
2397 case ocTableRefItemAll
:
2398 case ocTableRefItemHeaders
:
2399 case ocTableRefItemData
:
2400 case ocTableRefItemTotals
:
2401 case ocTableRefItemThisRow
:
2417 // mapped or translated name of AddIns
2418 OUString
aAddIn( t
->GetExternal() );
2419 bool bMapped
= mxSymbols
->isPODF(); // ODF 1.1 directly uses programmatical name
2420 if (!bMapped
&& mxSymbols
->hasExternals())
2422 ExternalHashMap::const_iterator iLook
= mxSymbols
->getReverseExternalHashMap().find( aAddIn
);
2423 if (iLook
!= mxSymbols
->getReverseExternalHashMap().end())
2425 aAddIn
= (*iLook
).second
;
2429 if (!bMapped
&& !mxSymbols
->isEnglish())
2430 LocalizeString( aAddIn
);
2431 rBuffer
.append( aAddIn
);
2435 AppendErrorConstant( rBuffer
, t
->GetError());
2444 SAL_WARN("formula.core", "FormulaCompiler::GetStringFromToken: unknown token type " << t
->GetType());
2449 rBuffer
.append( ' ');
2450 if ( bAllowArrAdvance
)
2453 t
= maArrIterator
.Next();
2460 void FormulaCompiler::AppendDouble( OUStringBuffer
& rBuffer
, double fVal
) const
2462 if ( mxSymbols
->isEnglish() )
2464 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
2465 rtl_math_StringFormat_Automatic
,
2466 rtl_math_DecimalPlaces_Max
, '.', true );
2470 SvtSysLocale aSysLocale
;
2471 ::rtl::math::doubleToUStringBuffer( rBuffer
, fVal
,
2472 rtl_math_StringFormat_Automatic
,
2473 rtl_math_DecimalPlaces_Max
,
2474 aSysLocale
.GetLocaleData().getNumDecimalSep()[0],
2479 void FormulaCompiler::AppendBoolean( OUStringBuffer
& rBuffer
, bool bVal
) const
2481 rBuffer
.append( mxSymbols
->getSymbol( bVal
? ocTrue
: ocFalse
) );
2484 void FormulaCompiler::AppendString( OUStringBuffer
& rBuffer
, const OUString
& rStr
)
2486 rBuffer
.append( '"');
2487 if ( lcl_UnicodeStrChr( rStr
.getStr(), '"' ) == nullptr )
2488 rBuffer
.append( rStr
);
2491 OUString aStr
= rStr
.replaceAll( "\"", "\"\"" );
2492 rBuffer
.append(aStr
);
2494 rBuffer
.append( '"');
2497 bool FormulaCompiler::NeedsTableRefTransformation() const
2499 // Currently only UI representations and OOXML export use Table structured
2500 // references. Not defined in ODFF.
2501 // Unnecessary to explicitly check for ODFF grammar as the ocTableRefOpen
2502 // symbol is not defined there.
2503 return mxSymbols
->getSymbol( ocTableRefOpen
).isEmpty() || FormulaGrammar::isPODF( meGrammar
);
2506 void FormulaCompiler::UpdateSeparatorsNative(
2507 const OUString
& rSep
, const OUString
& rArrayColSep
, const OUString
& rArrayRowSep
)
2509 NonConstOpCodeMapPtr xSymbolsNative
;
2510 lcl_fillNativeSymbols( xSymbolsNative
);
2511 xSymbolsNative
->putOpCode( rSep
, ocSep
, nullptr);
2512 xSymbolsNative
->putOpCode( rArrayColSep
, ocArrayColSep
, nullptr);
2513 xSymbolsNative
->putOpCode( rArrayRowSep
, ocArrayRowSep
, nullptr);
2516 void FormulaCompiler::ResetNativeSymbols()
2518 NonConstOpCodeMapPtr xSymbolsNative
;
2519 lcl_fillNativeSymbols( xSymbolsNative
, true);
2520 lcl_fillNativeSymbols( xSymbolsNative
);
2523 void FormulaCompiler::SetNativeSymbols( const OpCodeMapPtr
& xMap
)
2525 NonConstOpCodeMapPtr xSymbolsNative
;
2526 lcl_fillNativeSymbols( xSymbolsNative
);
2527 xSymbolsNative
->copyFrom( *xMap
);
2531 OpCode
FormulaCompiler::NextToken()
2535 OpCode eOp
= mpToken
->GetOpCode();
2536 // There must be an operator before a push
2537 if ( (eOp
== ocPush
|| eOp
== ocColRowNameAuto
) &&
2538 !( (eLastOp
== ocOpen
) || (eLastOp
== ocSep
) ||
2539 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)) )
2540 SetError( FormulaError::OperatorExpected
);
2541 // Operator and Plus => operator
2542 if (eOp
== ocAdd
&& (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2543 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2545 FormulaCompilerRecursionGuard
aRecursionGuard( nRecursion
);
2550 // Before an operator there must not be another operator, with the
2551 // exception of AND and OR.
2552 if ( eOp
!= ocAnd
&& eOp
!= ocOr
&&
2553 (SC_OPCODE_START_BIN_OP
<= eOp
&& eOp
< SC_OPCODE_STOP_BIN_OP
)
2554 && (eLastOp
== ocOpen
|| eLastOp
== ocSep
||
2555 (SC_OPCODE_START_BIN_OP
<= eLastOp
&& eLastOp
< SC_OPCODE_STOP_UN_OP
)))
2557 SetError( FormulaError::VariableExpected
);
2558 if ( bAutoCorrect
&& !pStack
)
2560 if ( eOp
== eLastOp
|| eLastOp
== ocOpen
)
2561 { // throw away duplicated operator
2562 aCorrectedSymbol
.clear();
2567 sal_Int32 nPos
= aCorrectedFormula
.getLength();
2571 sal_Unicode c
= aCorrectedFormula
[ nPos
];
2575 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2576 { // >= instead of =>
2577 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2578 OUString( mxSymbols
->getSymbolChar(ocGreater
) ) );
2579 aCorrectedSymbol
= OUString(c
);
2584 if ( c
== mxSymbols
->getSymbolChar( ocEqual
) )
2585 { // <= instead of =<
2586 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2587 OUString( mxSymbols
->getSymbolChar(ocLess
) ) );
2588 aCorrectedSymbol
= OUString(c
);
2591 else if ( c
== mxSymbols
->getSymbolChar( ocGreater
) )
2592 { // <> instead of ><
2593 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2594 OUString( mxSymbols
->getSymbolChar(ocLess
) ) );
2595 aCorrectedSymbol
= OUString(c
);
2600 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2601 { // *- instead of -*
2602 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2603 OUString( mxSymbols
->getSymbolChar(ocMul
) ) );
2604 aCorrectedSymbol
= OUString(c
);
2609 if ( c
== mxSymbols
->getSymbolChar( ocSub
) )
2610 { // /- instead of -/
2611 aCorrectedFormula
= aCorrectedFormula
.replaceAt( nPos
, 1,
2612 OUString( mxSymbols
->getSymbolChar(ocDiv
) ) );
2613 aCorrectedSymbol
= OUString(c
);
2624 // Nasty, ugly, horrific, terrifying... significant whitespace...
2625 if (eOp
== ocSpaces
&& FormulaGrammar::isExcelSyntax( meGrammar
))
2627 // Fake an intersection op as last op for the next round, but at
2628 // least roughly check if it could make sense at all.
2629 FormulaToken
* pPrev
= maArrIterator
.PeekPrevNoSpaces();
2630 if (pPrev
&& isPotentialRangeType( pPrev
, false, false))
2632 FormulaToken
* pNext
= maArrIterator
.PeekNextNoSpaces();
2633 if (pNext
&& isPotentialRangeType( pNext
, false, true))
2634 eLastOp
= ocIntersect
;
2647 void FormulaCompiler::PutCode( FormulaTokenRef
& p
)
2649 if( pc
>= FORMULA_MAXTOKENS
- 1 )
2651 if ( pc
== FORMULA_MAXTOKENS
- 1 )
2653 p
= new FormulaByteToken( ocStop
);
2658 SetError( FormulaError::CodeOverflow
);
2661 if (pArr
->GetCodeError() != FormulaError::NONE
&& mbJumpCommandReorder
)
2663 ForceArrayOperator( p
);
2670 bool FormulaCompiler::HandleExternalReference( const FormulaToken
& /*_aToken*/)
2675 bool FormulaCompiler::HandleRange()
2680 bool FormulaCompiler::HandleColRowName()
2685 bool FormulaCompiler::HandleDbData()
2690 bool FormulaCompiler::HandleTableRef()
2695 void FormulaCompiler::CreateStringFromSingleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2699 void FormulaCompiler::CreateStringFromDoubleRef( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2703 void FormulaCompiler::CreateStringFromIndex( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2707 void FormulaCompiler::CreateStringFromMatrix( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2711 void FormulaCompiler::CreateStringFromExternal( OUStringBuffer
& /*rBuffer*/, const FormulaToken
* /*pToken*/) const
2715 void FormulaCompiler::LocalizeString( OUString
& /*rName*/ ) const
2719 formula::ParamClass
FormulaCompiler::GetForceArrayParameter( const FormulaToken
* /*pToken*/, sal_uInt16
/*nParam*/ ) const
2721 return ParamClass::Unknown
;
2724 void FormulaCompiler::ForceArrayOperator( FormulaTokenRef
const & rCurr
)
2726 if (rCurr
->GetInForceArray() != ParamClass::Unknown
)
2727 // Already set, unnecessary to evaluate again. This happens by calls to
2728 // CurrentFactor::operator=() while descending through Factor() and
2729 // then ascending back (and down and up, ...),
2730 // CheckSetForceArrayParameter() and later PutCode().
2733 const OpCode eOp
= rCurr
->GetOpCode();
2734 const StackVar eType
= rCurr
->GetType();
2735 bool bInlineArray
= false;
2736 if (!(eOp
!= ocPush
&& (eType
== svByte
|| eType
== svJump
))
2737 && !(bInlineArray
= (eOp
== ocPush
&& eType
== svMatrix
)))
2740 // Return class for inline arrays and functions returning array/matrix.
2741 // It's somewhat unclear what Excel actually does there and in
2742 // ECMA-376-1:2016 OOXML mentions "call to ... shall be an array formula"
2743 // only for FREQUENCY() and TRANSPOSE() but not for any other function
2744 // returning array/matrix or inline arrays, though for the latter has one
2745 // example in 18.17.2 Syntax:
2746 // "SUM(SQRT({1,2,3,4})) returns 6.14 when entered normally". However,
2747 // these need to be treated similar but not as ParamClass::ForceArray
2748 // (which would contradict the example in
2749 // https://bugs.documentfoundation.org/show_bug.cgi?id=122301#c19 and A6 of
2750 // https://bugs.documentfoundation.org/show_bug.cgi?id=133260#c10 ).
2752 // commit d0ded163d8e93dc5b10d7a7c9bdab1d0a6a50bac
2753 // commit 5413c8871dec08eff19f514f5f391b946a45c86c
2754 constexpr ParamClass eArrayReturn
= ParamClass::ForceArrayReturn
;
2758 // rCurr->SetInForceArray() can not be used with ocPush.
2759 if (pCurrentFactorToken
&& pCurrentFactorToken
->GetInForceArray() == ParamClass::Unknown
)
2761 // Propagate to caller as if a function returning an array/matrix
2762 // was called (see also below).
2763 pCurrentFactorToken
->SetInForceArray( eArrayReturn
);
2768 if (!pCurrentFactorToken
|| (pCurrentFactorToken
.get() == rCurr
.get()))
2770 if (!pCurrentFactorToken
&& mbMatrixFlag
)
2772 // An array/matrix formula acts as ForceArray on all top level
2773 // operators and function calls, so that can be inherited properly
2775 rCurr
->SetInForceArray( ParamClass::ForceArray
);
2780 // Inherited parameter class.
2781 const formula::ParamClass eForceType
= pCurrentFactorToken
->GetInForceArray();
2782 if (eForceType
== ParamClass::ForceArray
|| eForceType
== ParamClass::ReferenceOrRefArray
)
2784 // ReferenceOrRefArray was set only if in ForceArray context already,
2785 // it is valid for the one function only to indicate the preferred
2786 // return type. Propagate as ForceArray if not another parameter
2787 // handling ReferenceOrRefArray.
2788 if (nCurrentFactorParam
> 0
2789 && (GetForceArrayParameter( pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1))
2790 == ParamClass::ReferenceOrRefArray
))
2791 rCurr
->SetInForceArray( ParamClass::ReferenceOrRefArray
);
2793 rCurr
->SetInForceArray( ParamClass::ForceArray
);
2796 else if (eForceType
== ParamClass::ReferenceOrForceArray
)
2798 // Inherit further only if the return class of the nested function is
2799 // not Reference. Else flag as suppressed.
2800 if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) != ParamClass::Reference
)
2801 rCurr
->SetInForceArray( eForceType
);
2803 rCurr
->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray
);
2807 if (nCurrentFactorParam
<= 0)
2810 // Actual current parameter's class.
2811 const formula::ParamClass eParamType
= GetForceArrayParameter(
2812 pCurrentFactorToken
.get(), static_cast<sal_uInt16
>(nCurrentFactorParam
- 1));
2813 if (eParamType
== ParamClass::ForceArray
)
2814 rCurr
->SetInForceArray( eParamType
);
2815 else if (eParamType
== ParamClass::ReferenceOrForceArray
)
2817 if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) != ParamClass::Reference
)
2818 rCurr
->SetInForceArray( eParamType
);
2820 rCurr
->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray
);
2823 // Propagate a ForceArrayReturn to caller if the called function
2824 // returns one and the caller so far does not have a stronger array
2826 if (pCurrentFactorToken
->GetInForceArray() == ParamClass::Unknown
)
2828 if (IsMatrixFunction( eOp
))
2829 pCurrentFactorToken
->SetInForceArray( eArrayReturn
);
2830 else if (GetForceArrayParameter( rCurr
.get(), SAL_MAX_UINT16
) == ParamClass::ForceArrayReturn
)
2831 pCurrentFactorToken
->SetInForceArray( ParamClass::ForceArrayReturn
);
2835 void FormulaCompiler::CheckSetForceArrayParameter( FormulaTokenRef
const & rCurr
, sal_uInt8 nParam
)
2837 if (!pCurrentFactorToken
)
2840 nCurrentFactorParam
= nParam
+ 1;
2842 ForceArrayOperator( rCurr
);
2845 void FormulaCompiler::PushTokenArray( FormulaTokenArray
* pa
, bool bTemp
)
2847 if ( bAutoCorrect
&& !pStack
)
2848 { // don't merge stacked subroutine code into entered formula
2849 aCorrectedFormula
+= aCorrectedSymbol
;
2850 aCorrectedSymbol
.clear();
2852 FormulaArrayStack
* p
= new FormulaArrayStack
;
2855 p
->nIndex
= maArrIterator
.GetIndex();
2856 p
->mpLastToken
= mpLastToken
;
2860 maArrIterator
= FormulaTokenArrayPlainIterator( *pArr
);
2863 } // namespace formula
2865 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */